目录
二叉树的非递归遍历
我们一般使用递归遍历二叉树,但借助栈我们也可以通过非递归遍历二叉树。
如上图所示,先遍历根节点和其左子树,将遍历到的节点入栈,之后再取出栈内的节点遍历其右子树
1.前序遍历:
按照上图的步骤来实现,前序遍历(根-->左-->右),在节点入栈的时候打印该节点即可:
//非递归实现前序遍历 public void preorderTraversalNor(TreeNode root) { //辅助遍历二叉树的栈 Stack<TreeNode> stack = new Stack<>(); //根节点入栈 stack.push(root); TreeNode cur = root; //栈内取出的元素的右节点不为空且栈不为空 while (cur != null || !stack.empty()) { //根节点或左节点不为空 while (cur != null) { //节点入栈 stack.push(cur); //打印节点的值 System.out.print(cur.val + " "); //cur指向节点的左节点 cur = cur.left; } //cur指向栈顶节点的右节点且该节点出栈 cur = stack.pop().right; } }
2.中序遍历:
中序遍历(左-->根-->右),故应该在节点的左节点为空或遍历过节点的左子树时,再打印栈顶存放的该节点的父节点:
//非递归实现中序遍历 public void inorderTraversalNor(TreeNode root) { TreeNode cur = root; Stack<TreeNode> stack = new Stack<>(); while (cur != null && !stack.empty()) { while (cur != null) { stack.push(cur); cur = cur.left; } TreeNode top = stack.pop(); System.out.print(top.val + " "); cur = top.right; } }
3.后序遍历:
后续遍历(左-->右-->根),故应该在节点的左右节点都为空或左右节点均遍历过时,再打印栈顶存放的该节点的父节点:
//非递归的后续遍历 public void orderTraversalNor(TreeNode root) { //用来记录被打印过的子树的根节点 TreeNode prev = null; TreeNode cur = root; Stack<TreeNode> stack = new Stack<>(); while (cur != null || !stack.empty()) { while (cur != null) { stack.push(cur); cur = cur.left; } TreeNode top = stack.peek(); //右节点为空或右边已经被打印过了 if (top.right == null || top.right == prev) { System.out.print(top.val + " "); stack.pop(); //记录被打印过的子树的根节点 prev = top; } else { cur = top.right; } } }
注意:
1.因为根节点是最后打印的,故获取栈内节点的右节点时并不能将栈内的节点出栈。
2.因为获取栈内节点的右节点时没有将栈内节点出栈,故当遍历完该节点的右节点后才会轮到该节点出栈,此时若该节点的右节点不为空,就需要判断该节点的右节点是否被打印过了,不然就会再遍历一遍该节点的右边。
二叉树的最近公共祖先
上图中B,C的最近公共祖先是A;D,C的最近公共祖先是A;E,F的最近公共祖先是B。
获取二叉树树的最近公共祖先可以通过下面两种方法:
1.递归实现
子问题的思想,分p和q有一个在根节点,p和q都在左子树,p和q都在右子树讨论:
public class Test { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { //根节点为空,没有最近公共祖先 if (root == null || p == null || q == null) { return null; } //p为根节点,最近公共祖先为p;q为根节点,最近公共祖先为q if (root == p || root == q) { return root; } //左子树找p,q TreeNode leftTree = lowestCommonAncestor2(root.left, p, q); //右子树找p,q TreeNode rightTree = lowestCommonAncestor(root.right, p, q); if (leftTree != null && rightTree != null) { //一个在左,一个在右,最近公共祖先为该子树的根节点 return root; } else if (leftTree == null) { //都在右子树,最近公共祖先为p或q中辈分大的那个 return rightTree; } else if (rightTree == null) { //都在左子树,最近公共祖先为p,q中辈分大的那个 return leftTree; } else { //都没找到,返回null return null; } } }
2.非递归实现
用两个栈分别存放两个节点到根节点路径上的所有节点,将较长栈出栈直达两个栈的大小相等,最后同时出栈比较出栈元素,两个栈第一个相同的出栈元素即为最近公共祖先:
.
public class Test { //最近公共祖先 public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if (root == null || p == null || q == null) { return null; } Stack<TreeNode> stack1 = new Stack<>(); Stack<TreeNode> stack2 = new Stack<>(); //stack1存放root到p路径上的所有节点 getPath(root, p, stack1); //stack2存放root到q路径上的所有节点 getPath(root, q, stack2); int size1 = stack1.size(); int size2 = stack2.size(); //将路径比较长的去掉长出来的那段尾巴 if (size1 > size2) { int size = size1 - size2; while (size > 0) { stack1.pop(); size--; } } else { int size = size2 - size1; while (size > 0) { stack2.pop(); size--; } stack2.pop(); } //此时同时比较出栈的元素就能得到最近公共祖先 while (stack1.peek() != stack2.peek()) { stack1.pop(); stack2.pop(); } return stack1.peek(); } private boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) { if (root == null || node == null) { return false; } stack.push(root); if (root == node) { return true; } boolean flg1 = getPath(root.left, node, stack); if (flg1) { return true; } boolean flg2 = getPath(root.right, node, stack); if (flg2) { return true; } stack.pop(); return false; } }