1、二叉树遍历
1、 采用传统的递归 (O(n)的空间复杂度)
public void preOrder(TreeNode root) { if(root == null) return; System.out.println(root.val); preOrder(root.left); preOrder(root.right); } public void inOrder(TreeNode root) { if(root == null) return; preOrder(root.left); System.out.println(root.val); preOrder(root.right); } public void postOrder(TreeNode root) { if(root == null) return; preOrder(root.left); preOrder(root.right); System.out.println(root.val); }
2、采用 Stack + 迭代的方式 (O(n)的空间复杂度)
①、先序遍历
public void preOrder2(TreeNode root) { if(root == null) return; Stack<TreeNode> stack = new Stack<>(); stack.add(root); while(!stack.isEmpty()) { TreeNode node = stack.pop(); System.out.println(node.val); if(node.right != null) stack.add(node.right); if(node.left != null) stack.add(node.left); } }
优化: Stack 只用于存储 Right 节点。
public List<Integer> preorderTraversal(TreeNode root) { List<Integer> result = new ArrayList<>(); if(root == null) return result; // 只存储右节点 Stack<TreeNode> rightStack = new Stack<>(); TreeNode node = root; while(node != null) { result.add(node.val); if(node.right != null) rightStack.add(node.right); node = node.left; if(node == null && !rightStack.isEmpty()) node = rightStack.pop(); } return result; }
②、中序遍历
public void inOrder2(TreeNode root) { if(root == null) return; Stack<TreeNode> stack = new Stack<>(); stack.add(root); while(!stack.isEmpty()) { TreeNode node = stack.peek(); if(node.left != null) { stack.push(node.left); // 左节点以入栈,则标记为空,否则死循环 node.left = null; } else { System.out.println(stack.pop().val); if(node.right != null)// 插入右节点 stack.push(node.right); } } }
优化
public void inOrder3(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); TreeNode cur = root; while(cur != null || !stack.isEmpty()) { while(cur != null) { // 将 cur 的左斜子树全部添加 stack.add(cur); cur = cur.left; } cur = stack.pop(); System.out.println(cur.val); cur = cur.right; } }
③、后续遍历
a、采用 Stack 进行压栈操作,同时采用一个 Map 记录该节点的右孩子是否被访问过
public void postOrder2(TreeNode root) { // 用于标记结点的右孩子已经被遍历过 Map<Integer, Integer> map = new HashMap<Integer, Integer>(); Stack<TreeNode> stack = new Stack<>(); TreeNode node; while(root != null || !stack.isEmpty()) { while(root != null) { stack.push(root); map.put(root.val, 0); // 标记未访问 root = root.left; } if(!stack.empty()) { node = stack.peek(); // 此时左孩子为空, 若右孩子不为空且未被访问过,则访问右孩子 if(node.right != null && map.get(node.val) != 1) { root = node.right; map.put(node.val, 1); //表示 node 结点的右孩子已经被遍历过 } else { // 若右孩子为空,则访问此节点. stack.pop(); System.out.println(node.val); } } } }
b、采用一个指针记录当前右孩子是否访问过
public void postOrder3(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); TreeNode pre = null; // 记录前一个访问的节点,用于判断右孩子是否访问过 TreeNode node; while(root != null || !stack.isEmpty()) { while(root != null) { stack.push(root); root = root.left; } if(!stack.isEmpty()) { node = stack.peek(); if(node.right != null && node.right != pre) root = node.right; else { System.out.println(node.val); pre = node; stack.pop(); } } } }
c、前序遍历比较好写,利用伪前序遍历方法的逆序来写后续遍历
伪前序:根-->右-->左
后续:左-->右-->根
public void postOrder4(TreeNode root) { if(root == null) return; // 用于记录 根-->右-->左 的节点顺序 List<TreeNode> list = new ArrayList<TreeNode>(); Stack<TreeNode> stack = new Stack<>(); stack.add(root); TreeNode node; while(!stack.isEmpty()) { node = stack.pop(); list.add(node); if(node.left != null) stack.add(node.left); if(node.right != null) stack.add(node.right); } Collections.reverse(list); for(TreeNode tmp: list) System.out.println(tmp.val ); }
3、Morris Traversal 方法线索二叉树,只需要O(1)空间,而且同样可以在O(n)时间内完成。
引自: https://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html
主要思想是:
根据中序遍历序列将叶子节点添加右索引,指向祖先节点。线索查找时根据条件输出即可。
前序遍历
void preOrderMorrisTraversa(TreeNode root) { TreeNode cur = root; TreeNode pre = null; while(cur != null) { if(cur.left == null) { System.out.println(cur.val); cur = cur.right; } else { pre = cur.left; while(pre.right != null && pre.right != cur) pre = pre.right; if(pre.right == null) { // 添加右线索 System.out.println(cur.val); pre.right = cur; cur = cur.left; } else { pre.right = null; cur = cur.right; } } } }
中序遍历
void inorderMorrisTraversal(TreeNode root) { TreeNode cur = root; TreeNode pre = null; while(cur != null) { if(cur.left == null) { System.out.println(cur.val + "-->"); cur = cur.right; } else { pre = cur.left; while(pre.right != null && pre.right != cur) pre = pre.right; if(pre.right == null) { pre.right = cur; cur = cur.left; } else { // pre.right == cur , 此时断开线索 pre.right = null; System.out.println(cur.val + "--->"); cur = cur.right; } } } }
后序遍历
void postorderMorrisTraversal(TreeNode root) { TreeNode dump = new TreeNode(0); dump.left = root; TreeNode cur = dump; TreeNode pre = null; while(cur != null) { if(cur.left == null) { cur = cur.right; } else { pre = cur.left; while(pre.right != null && pre.right != cur) pre = pre.right; if(pre.right == null) { pre.right = cur; cur = cur.left; } else { // 将线索的一条右斜支链逆序输出 printReverse(cur.left, pre); pre.right = null; cur = cur.right; } } } } private void printReverse(TreeNode from, TreeNode to) { reverse(from, to); TreeNode p = to; while(true) { System.out.println(p.val + " "); if(p == from) break; p = p.right; } reverse(to, from); } private void reverse(TreeNode from, TreeNode to) { if(from == to) return; TreeNode x = from; TreeNode y = from.right; while(true) { TreeNode z = y.right; y.right = x; x = y; y = z; if(x == to) break; } }