BST Traversal


BST Traversal [Medium]

  • Write three functions (in-order, pre-order, and post-order traversal) that takes in a Binary Search Tree (BST) and an empty array, traverse the BST add the node’s value to the input array and return the array.

Sample Input

tree =              10
	              /     \
		        5	      15
	          /   \          \
		     2     6          22
	       /
		 1

Sample Output

in_order = [1, 2, 5, 6, 10, 15, 22]
pre_order = [10, 5, 2, 1, 6, 15, 22]
post_order = [1, 2, 6s, 5, 22, 15, 10]

Hints

Hint 1
Realize that in-order traversal simply means traversing left nodes before traversing current nodes before traversing right nodes.Try implementing this algorithm recursively by calling the inOrderTraverse method on a left node,then appending the current node's value to the input array,and then calling the inOrderTraverse method on a right node.


Hint 2
Apply the same logic described in Hint #1 for the two other traversal methods,but change the order in which you do things.


Method 1(Recursive)

【 O(n)time | O(h) space 】
package NewArray;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zhengstars
 * @date 2023/01/06
 */
public class BSTTraversal {

    public class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode() {}
        TreeNode(int val) { this.val = val; }
        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }

    /**
     * Mid-Order traversal
     * left mid right
     * @param root
     * @return
     */
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        inOrder(list,root);
        return list;
    }

    public static void inOrder(List<Integer> list, TreeNode root ){
        if(null == root){
            return;
        }
        inOrder(list,root.left);
        list.add(root.val);
        inOrder(list,root.right);
    }

    /**
     * Pre-Order traversal
     * mid left right
     * @param root
     * @return
     */
    public List<Integer> preTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        preOrder(list,root);
        return list;
    }

    public static void preOrder(List<Integer> list, TreeNode root ){
        if(null == root){
            return;
        }
        list.add(root.val);
        preOrder(list,root.left);
        preOrder(list,root.right);
    }



    /**
     * Post-order traversal
     * left right mid
     * @param root
     * @return
     */
    public List<Integer> postTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        postOrder(list,root);
        return list;
    }

    public static void postOrder(List<Integer> list, TreeNode root ){
        if(null == root){
            return;
        }
        postOrder(list,root.left);
        postOrder(list,root.right);
        list.add(root.val);
    }
}

Method 2(Iteration)

【 O(n)time | O(h) space 】
package NewArray;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @author zhengstars
 * @date 2023/01/06
 */
class TreeNodeTraversal2 {
    static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode() {}
        TreeNode(int val) { this.val = val; }
        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }

    public static List<Integer> inorderTraversal(TreeNode root) {
        // Stack first in and then out
        // Traversal in the middle order, out-of-stack order: left root right; stack order: right root left
        //中序遍历,出栈顺序:左根右; 入栈顺序:右根左
        List<Integer> list = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();

        // Root is empty and stack is empty. Traversal ends.
        while(null != root || !stack.isEmpty()){
            // First root and then left into the stack
            while(null != root){
                stack.push(root);
                root = root.left;
            }

            // Root==null at this time indicates that the root in the previous step does not have a left subtree.
            // 1. Execute the left exit stack. Because root==null at this time, root.right must be null
            // 2. Execute the next outer while code block and root it out of the stack. Root.right may exist at this time
            // 3a. If root.right exists, enter the stack right and then exit the stack.
            // 3b. If root.right does not exist, repeat step 2
            root = stack.pop();
            list.add(root.val);
            root = root.right;
        }

        return list;
    }

    public static List<Integer> preorderTraversal(TreeNode root) {

        List<Integer> list = new ArrayList<Integer>();
        // Create a stack to store the nodes that will be traversed
        Stack<TreeNode> stack = new Stack<>();
        // If the root node is not empty, press the root node into the stack
        if (root != null) {
            stack.push(root);
        }

        // Execute a loop when the stack is not empty
        while (!stack.empty()) {
            // Remove the elements at the top of the stack
            TreeNode node = stack.pop();

            list.add(node.val);


            // **Because the order of entering the stack is the right and left root, **
            // **you need to judge the right to enter the stack before judging the left**

            // If the current node has a right child node, press the right child node into the stack
            if (node.right != null) {
                stack.push(node.right);
            }
            // If the current node has a left child node, press the left child node into the stack
            if (node.left != null) {
                stack.push(node.left);
            }
        }
        return list;
    }

    public static List<Integer> postorderTraversal(TreeNode root) {

        List<Integer> list = new ArrayList<Integer>();

        if(root == null){
            return list;
        }

        // Create a stack to store the nodes that will be traversed
        Stack<TreeNode> stack = new Stack<>();

        // Variable is used to record whether the right subtree has been accessed
        TreeNode preNode = null;

        //Main ideas:
        //Because after the access to a child tree is completed, it has to be traced back to its parent node

        //Therefore, prev can be used to record the access history,
        // which can be used to determine whether the last accessed node is a right subtree when backtracking to the parent node.
        while(null != root || !stack.isEmpty()){
            while(null != root){
                stack.push(root);
                root = root.left;
            }
            //For the elements popping up from the stack, the left subtree must have been accessed.
            root = stack.pop();

            //What we need to determine now is whether there is a right subtree, or whether the right subtree has been visited.
            //If there is no right subtree, or if the right subtree is finished, that is, when the last node visited is the right child node
            //Indicates that the current node can be accessed
            if(null == root.right || root.right == preNode){
                list.add(root.val);
                //Update the historical access record so that when backtracking, the parent node can determine whether the access to the right child tree is complete.
                preNode = root;
                root = null;
            }else{
                //If the right subtree is not accessed, press the current node on the stack and access the right subtree
                stack.push(root);
                root = root.right;
            }
        }

        return list;
    }


    public static void main(String[] args) {
        TreeNode tree = new TreeNode(10);

        TreeNode tree1 = new TreeNode(5);
        tree1.left = new TreeNode(2);
        tree1.right = new TreeNode(6);

        TreeNode tree2 = new TreeNode(15);
        tree2.left = new TreeNode(12);
        tree2.right = new TreeNode(22);

        tree.left = tree1;
        tree.right = tree2;

        System.out.println(preorderTraversal(tree));
    }
}

Method 3(Morris)

【 O(n)time | O(1) space 】
package NewArray;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

/**
 * @author zhengstars
 * @date 2023/01/09
 */
public class BSTTraversal4 {

    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode() {}
        TreeNode(int val) { this.val = val; }
        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }


    /**
     * Mid-order traversal
     * You can only come to your own node once and print directly.
     * The node that can come to you twice will only print the second time.
     * @param root
     * @return
     */
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(null == root){
            return list;
        }

        TreeNode cur = root;

        TreeNode morrisRight = null;

        // Cur is empty to stop traversing
        while(null != cur){
            //morrisRight is cur's left child.
            morrisRight = cur.left;

            // There is a left subtree
            if(null != morrisRight){

                // After while finished running,
                // morrisRight came to the position of the rightmost child in the left subtree of cur
                // Find the rightmost node of the left subtree
                while(null != morrisRight.right && morrisRight.right != cur){
                    morrisRight = morrisRight.right;
                }

                // Come to the location of cur for the first time
                if(null == morrisRight.right){
                    morrisRight.right = cur;
                    cur = cur.left;
                    continue;// The first time you come in cur, just start the continue,while loop again.
                }else{//When came to cur,morrisRight for the second time, the right pointer was broken.
                    //Enter cur for the second time and execute it directly.
                    morrisRight.right = null;
                }
            }
            // You can only come to your own node once and print directly.
            // The node that can come to you twice will only print the second time.
            list.add(cur.val);
            //If the left child is empty, Then directly access the right subtree of the node
            cur = cur.right;
        }
        return list;
    }

    /**
     * The node print that appears for the first time, that is
     * a.There is no direct printing of the left node, and the description of the left node is printed twice.
     * b.Come to cur printing for the first time
     * @param root
     * @return
     */
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(null == root){
            return list;
        }

        TreeNode cur = root;

        TreeNode morrisRight = null;

        // Cur is empty to stop traversing
        while(null != cur){
            //morrisRight is cur's left child.
            morrisRight = cur.left;

            // There is a left subtree
            if(null != morrisRight){

                // After while finished running,
                // morrisRight came to the position of the rightmost child in the left subtree of cur
                // Find the rightmost node of the left subtree
                while(null != morrisRight.right && morrisRight.right != cur){
                    morrisRight = morrisRight.right;
                }

                // Come to the location of cur for the first time
                if(null == morrisRight.right){
                    //  Come to the cur node for the first time
                    list.add(cur.val);

                    morrisRight.right = cur;
                    cur = cur.left;
                    continue;// The first time you come in cur, just start the continue,while loop again.
                }else{//When came to cur,morrisRight for the second time, the right pointer was broken.
                    //Enter cur for the second time and execute it directly.
                    morrisRight.right = null;
                }
            }else{
                // Only can come to yourself once.
                list.add(cur.val);
            }
            //If the left child is empty, Then directly access the right subtree of the node
            cur = cur.right;
        }
        return list;
    }


    public static List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(null == root){
            return list;
        }

        TreeNode cur = root;

        TreeNode morrisRight = null;

        // Cur is empty to stop traversing
        while(null != cur){
            //morrisRight is cur's left child.
            morrisRight = cur.left;

            // There is a left subtree
            if(null != morrisRight){

                // After while finished running,
                // morrisRight came to the position of the rightmost child in the left subtree of cur
                // Find the rightmost node of the left subtree
                while(null != morrisRight.right && morrisRight.right != cur){
                    morrisRight = morrisRight.right;
                }

                // Come to the location of cur for the first time
                if(null == morrisRight.right){
                    morrisRight.right = cur;
                    cur = cur.left;
                    continue;// The first time you come in cur, just start the continue,while loop again.
                }else{//When came to cur,morrisRight for the second time, the right pointer was broken.
                    //Enter cur for the second time and execute it directly.
                    morrisRight.right = null;

                    // Print the right boundary of the left subtree of cur points in reverse order
                    reversePrintEdge(cur.left,list);
                }
            }
            //If the left child is empty, Then directly access the right subtree of the node
            cur = cur.right;
        }
        // Finally, print the right boundary of the whole tree in reverse order.
        reversePrintEdge(root,list);

        return list;
    }

    /**
     * Take head as the head of the tree, reverse print its boundary
     * @param head
     * @param list
     */
    private static void reversePrintEdge(TreeNode head, List<Integer> list) {
        // Get the tail pointer, which is the rightmost point.
        TreeNode tail = reverse(head);
        // Will print from the tail pointer
        TreeNode cur = tail;
        while(cur != null) {
            list.add(cur.val);
            cur = cur.right;
        }
        // Reverse it at this time. Restore the original tree
        reverse(tail);
    }

    /**
     * Similar to the reverse change of the pointer of a single linked list,
     * the reverse of a single linked list
     * @param from
     * @return
     */
    private static TreeNode reverse(TreeNode from) {
        TreeNode pre = null;
        TreeNode next = null;
        while(from != null) {
            // From is not equal to null here, because after conversion to next, the last one is null
            next = from.right;
            from.right = pre;
            pre = from;
            from = next;
        }

        return pre;
    }

    public static void main(String[] args) {
        TreeNode tree = new TreeNode(1);

        TreeNode tree1 = new TreeNode(2);
        TreeNode tree2 = new TreeNode(3);

        TreeNode tree4 = new TreeNode(4);
        tree4.left = new TreeNode(8);
        tree4.right = new TreeNode(9);

        tree1.left = tree4;
        tree1.right = new TreeNode(5);

        TreeNode tree5 = new TreeNode(7);
        tree5.left = new TreeNode(10);
        tree5.right = new TreeNode(11);

        tree2.left = new TreeNode(6);
        tree2.right = tree5;

        tree.left = tree1;
        tree.right = tree2;

        List<Integer> list =  postorderTraversal(tree);

        System.out.println(list);
    }
}




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • 2379. Minimum Recolors to Get K Consecutive Black Blocks
  • 2471. Minimum Number of Operations to Sort a Binary Tree by Level
  • 1387. Sort Integers by The Power Value
  • 2090. K Radius Subarray Averages
  • 2545. Sort the Students by Their Kth Score