树的遍历合辑

前序遍历

遍历顺序为root-->left-->right。递归方法省略。

方法一:

分析:采用栈实现,每次将根节点压入栈,然后弹出栈顶元素,并把右节点和左节点依次压入栈。运行时间为2ms。代码如下:

public class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> rst = new LinkedList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if (root == null) return rst;
        stack.push(root);
        while (!stack.empty()) {
            TreeNode node = stack.pop();
            rst.add(node.val);
            if (node.right != null) {
                stack.push(node.right);
            }
            if (node.left != null) {
                stack.push(node.left);
            }
        }
        return rst;
    }
}

方法二:

分析:同样采用栈实现,但对于每个节点只将其右节点压入栈。运行时间为1ms。

public class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> rst = new LinkedList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode node = root;
        while (node != null || !stack.empty()) {
            rst.add(node.val);
            if (node.right != null) {
                stack.push(node.right);
            }
            node = node.left;
            if (node == null && !stack.empty()) {
                node = stack.pop();
            }
        }
        return rst;
    }
}

方法三:

分析:采用堆栈的空间复杂度为O(n),而采用Morris Traversal空间复杂度为O(1)。运行时间为1ms。

public class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> rst = new LinkedList<Integer>();
        TreeNode cur = root;
        while (cur != null) {
            if (cur.left == null) {
                rst.add(cur.val);
                cur = cur.right;
            } else {
                TreeNode prev = cur.left;
                while (prev.right != null && prev.right != cur) {
                    prev = prev.right;
                }
                if (prev.right == null) {
                    rst.add(cur.val);
                    prev.right = cur;
                    cur = cur.left;
                } else {
                    prev.right = null;
                    cur = cur.right;
                }
            }
        }
        return rst;
    }
}

 

中序遍历

遍历顺序为left-->root-->right。递归方法省略。

方法一:

分析:采用栈实现,运行时间为2ms。

public class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.empty()){
            while(cur != null){
                stack.add(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            list.add(cur.val);
            cur = cur.right;
        }
        return list;
    }
}

方法二:

分析:采用Morris Traversal方法,空间复杂度为O(1),时间复杂度为O(n)。运行时间为1ms。与前序遍历的Morris Traversal方法相同,只是将节点的值加入列表的位置不同。

public class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        TreeNode cur = root;
        while(cur != null){
            if (cur.left == null) {
                list.add(cur.val);
                cur = cur.right;
            } else {
                TreeNode prev = cur.left;
                while (prev.right != null && prev.right != cur) {
                    prev = prev.right;
                }
                if (prev.right == null) {
                    prev.right = cur;
                    cur = cur.left;
                } else {
                    list.add(cur.val);
                    prev.right = null;
                    cur = cur.right;
                }
            }
        }
        return list;
    }
}

 

后序遍历

遍历顺序为left-->right-->root。递归方法省略。

方法一:

分析:后序遍历相当于root-->right-->left的逆序遍历,因此可以向采用前序遍历的方法一按root-->right-->left顺序遍历,然后将结果reverse即可。reverse结果与在迭代过程中把每个元素添加在list首部等效。运行时间为2ms。

public class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> rst = new LinkedList<Integer>();
        if (root == null) return rst;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (! stack.empty()) {
            TreeNode temp = stack.pop();
            rst.add(0, temp.val);
            if (temp.left != null) stack.push(temp.left);
            if (temp.right != null) stack.push(temp.right);
        }
        return rst;
    }
}

方法二:

对应于前序遍历的方法二,即只将左节点放入栈中。有两种写法。

I:运行时间2ms。

public class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> rst = new LinkedList<Integer>();
        if (root == null) return rst;
        Stack<TreeNode> stack = new Stack<>();
        while (root != null || !stack.empty()) {
            if (root != null) {
                rst.add(0, root.val);
                stack.push(root.left);
                root = root.right;
            } else {
                root = stack.pop();
            }
        }
        return rst;
    }
}

II:运行时间为1ms。

public class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> rst = new LinkedList<Integer>();
        if (root == null) return rst;
        Stack<TreeNode> stack = new Stack<>();
        while (root != null || !stack.empty()) {
            rst.add(0, root.val);
            if (root.left != null) {
                stack.push(root.left);
            }
            root = root.right;
            if (root == null && !stack.empty()) {
                root = stack.pop();
            }
        }
        return rst;
    }
}

分析II比I快的原因是II在将元素压入栈时进行了判断,只将非空元素压入,因此压入和弹出的次数会比I更小。而栈的压入和弹出时间对本问题运行时间影响较大,因此II明显比I快。

方法三:

分析:采用Morris Traversal方法。对应于前序遍历方法三,只是将left和right互换。运行时间为1ms。

public class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> rst = new LinkedList<Integer>();
        TreeNode cur = root;
        while (cur != null) {
            if (cur.right == null) {
                rst.add(0, cur.val);
                cur = cur.left;
            } else {
                TreeNode prev = cur.right;
                while (prev.left != null && prev.left != cur) {
                    prev = prev.left;
                }
                if (prev.left == null) {
                    rst.add(0, cur.val);
                    prev.left = cur;
                    cur = cur.right;
                } else {
                    prev.left = null;
                    cur = cur.left;
                }
            }
        }
        return rst;
    }
}

在leetcode discuss中一些人提到根据前序遍历方法将节点值每次加入到结果列表的首部并不是严格意义上的后序遍历。对于一些在过程中就需要后序遍历的情况下这种方法并不适用。因此,考虑到代码的通用性,需要实现真正意义上的后序遍历。

方法四:

分析:使用一个节点记录上一个被访问的元素。对于每个节点,将其左节点全部压入栈。然后对于栈顶节点(栈顶节点的左子树都已经被访问过),判断其右节点是否为空或为上个被访问的元素,若是,则将栈顶节点弹出,否则对栈顶节点右节点进行同样的操作(将其左节点全部压入栈...)此方法运行时间为2ms。

public class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> rst = new LinkedList<Integer>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while (cur != null || !stack.empty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.peek();
            if (cur.right == null || cur.right == prev) {
                rst.add(cur.val);
                prev = cur;
                stack.pop();
                cur = null;
            } else {
                cur = cur.right;
            }
        }
        return rst;
    }
}

 

 

层序遍历

方法一:

分析:BFS(4ms),采用queue实现,每次queue内保存当前层的所有节点。

public class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> rst = new LinkedList<>();
        if (root == null) return rst;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while (q.peek() != null) {
            int size = q.size();
            List<Integer> tmp = new LinkedList<>();
            while (size-- > 0) {
                TreeNode temp = q.poll();
                tmp.add(temp.val);
                if (temp.left != null) q.offer(temp.left);
                if (temp.right != null) q.offer(temp.right);
            }
            rst.add(new LinkedList<Integer>(tmp));
        }
        return rst;
    }
}

方法二:

分析:DFS(2ms),采用递归方法实现,每次将节点放入其对应层次的list中。

public class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> rst = new LinkedList<>();
        if (root == null) return rst;
        dfs(root, rst, 0);
        return rst;
    }
    public void dfs(TreeNode node, List<List<Integer>> rst, int level) {
        if (rst.size() == level) {
            rst.add(new LinkedList<>());
        }
        rst.get(level).add(node.val);
        if (node.left != null) dfs(node.left, rst, level + 1);
        if (node.right != null) dfs(node.right, rst, level + 1);
    }
}

 

附注:Morris Traversal附加参考链接http://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html

posted on 2016-12-06 18:14  ShinningWu  阅读(235)  评论(0编辑  收藏  举报

导航