• [Daily Coding Problem 223] O(1) space in order traversal of a binary tree


    Typically, an implementation of in-order traversal of a binary tree has O(h) space complexity, where h is the height of the tree. Write a program to compute the in-order traversal of a binary tree using O(1) space.

    In-order traversal without recursion with O(h) space, where h is the tree's height.

    public List<Integer> inOrderTraversal(BinaryTreeNode root) {
        List<Integer> vals = new ArrayList<>();
        if(root == null) {
            return vals;
        }
        Stack<BinaryTreeNode> stack = new Stack<>();
        BinaryTreeNode curr = root;
    
        while(stack.size() > 0 || curr != null) {
            while(curr != null) {
                stack.push(curr);
                curr = curr.getLeft();
            }
            curr = stack.pop();
            vals.add(curr.getVal());
            curr = curr.getRight();
        }
        return vals;
    }

    O(1) space solution

    The reason that the stack solution requires O(h) space consumption is because we must have some way to traverse back to a current node after traversing its left subtree. To avoid this space consumption, we need to restructure the tree while we traverse it, so that going to the right will always go to the "correct" next node. Namely, right after visiting the rightmost node in a left subtree, we continue to visit this rightmost node's right child that will lead us back to the this left subtree's parent. 

    Take the binary tree below as an example. Starting from root 8, it has a left subtree. Before traversing its left subtree, we need to get the rightmost descendant of this subtree and set its right child to be the root node 8. Otherwise, we wouldn't have any way of coming back to root. So we set 7's right child to be 8. After this step, traverse the left subtree. For node 3, we do the same, setting 1's right child to be 3. Repeat this until a node has no left subtree. At this point, we know we need to add its value to the result list. In an in-order traversal, we visit a node's right child after visiting the node. So we go back to 3. Now here comes a problem. Since we are constrained with constant space, we do not know that 3's left subtree has been traversed. How do we check that this is the case? We apply the same logic here: trying to the rightmost descendant of 3's left child(1) points to 3. After finding out that 1's right child already points to 3, we know that we have already traversed the left subtree with a root node 1. At this point, we need to revert the changes made to node 1 by setting its right child back to null. Then add 3 to the result list and traverse 3's right subtree. 

    Algorithm:

    1. if the current node has no left subtree, visit it;

    2. if it has left subtree, make the rightmost descendant node's right child in its left subtree points to itself; 

    2(a). If this is already done before, revert the changes, visit the current node, then traverse its right subtree;

    2(b). If this has not been done, do the upate, then traverse the current node's left subtree;

    3. Repeat steps 1 and 2 until the current node becomes null.

    This approach uses O(1) space at the cost of slower runtime as we need to traverse each left subtrees of every node twice.

    public List<Integer> inOrderTraversalConstantSpace(BinaryTreeNode root) {
        List<Integer> vals = new ArrayList<>();
        BinaryTreeNode curr = root;
    
        while(curr != null) {
            //add val if there is no left node to go to
            if(curr.getLeft() == null) {
                vals.add(curr.getVal());
                curr = curr.getRight();
            }
            //make the rightmost descendant of curr's left child points to curr
            else {
                BinaryTreeNode rightMostDesc = curr.getLeft();
                while(rightMostDesc.getRight() != null && rightMostDesc.getRight() != curr) {
                    rightMostDesc = rightMostDesc.getRight();
                }
                if(rightMostDesc.getRight() == null) {
                    rightMostDesc.setRight(curr);
                    curr = curr.getLeft();
                }
                else {
                    rightMostDesc.setRight(null);
                    vals.add(curr.getVal());
                    curr = curr.getRight();
                }
            }
        }
        return vals;
    }

    This problem basically implements a single threaded binary tree. For more references on this, refer to Threaded Binary Tree: https://www.geeksforgeeks.org/threaded-binary-tree/

  • 相关阅读:
    方法引用(method reference)
    函数式接口
    Lambda 表达式
    LinkedList 源码分析
    ArrayList 源码分析
    Junit 学习笔记
    Idea 使用 Junit4 进行单元测试
    Java 定时器
    【干货】Mysql的"事件探查器"-之Mysql-Proxy代理实战一(安装部署与实战sql拦截与性能监控)
    python-flask框架web服务接口开发实例
  • 原文地址:https://www.cnblogs.com/lz87/p/11162989.html
Copyright © 2020-2023  润新知