Algorithm/Tip:

二叉树的循环遍历

二叉树的层序遍历:

二叉树的层序遍历,一般使用队列或者栈来实现。
先使用栈或者队列存储第一层的节点,然后在下次循环时取出上一层的节点,并存储下一层的节点。如何循环往复,循环到最后一层。

  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. public void levelTraverse(TreeNode root){
  17. Queue<TreeNode> q = new LinkedList<>();
  18. q.offer(root);
  19. while(!q.isEmpty()){
  20. int size = q.size();
  21. for(int i = 0; i < size; ++i){
  22. // 获取到每个 node,在得到 node 后,可由根据需求进行其他操作
  23. TreeNode node = q.poll();
  24. if(node.left != null){
  25. q.offer(node.left);
  26. }
  27. if(node.right != null){
  28. q.offer(node.right);
  29. }
  30. }
  31. // 完成一层的遍历,在这里可以计算其高度,也可以保存各层的节点
  32. }
  33. }

二叉树的前序遍历:

二叉树的循环中的前序遍历,个人了解到一版有两种方式,但是其都是使用栈实现的。
循环方式的前序遍历,就是先按照 根节点->左节点 这一条线一直遍历,并将当前节点保存到栈中,当遍历到左叶节点之后,然后在往上回溯,继续遍历遇到的右节点。

使用栈实现前序遍历:

前序遍历的特点是:根节点->左节点->右节点 。
栈的特点是先入后出,所以入栈的顺序应该:根节点->右节点->左节点;

  1. void preorder(TreeNode root){
  2. if(root == null){
  3. return;
  4. }
  5. Deque<TreeNode> stack = new LinkedList<>();
  6. // 根节点 入栈
  7. stack.push(root);
  8. while(!stack.isEmpty()){
  9. // 在循环中首先出栈的就是 根节点,然后依次入栈 右节点-> 左节点
  10. // 下次循环,弹出上次入栈的节点,由于入栈的顺序是 右节点->左节点;
  11. // 那么出栈的顺序就是:左节点->右节点
  12. TreeNode node = stack.pop();
  13. System.out.println(node == null ? "null" : node.val);
  14. // 右节点 入栈
  15. if(node.right != null){
  16. stack.push(node.right);
  17. }
  18. // 左节点 入栈
  19. if(node.left != null){
  20. stack.push(node.left);
  21. }
  22. }
  23. }

另一种方式:

  1. void preorder(TreeNode root){
  2. if(root == null){
  3. return;
  4. }
  5. // 使用双端队列来实现栈的功能
  6. Deque<TreeNode> stack = new LinkedList<>();
  7. TreeNode node = root;
  8. while(!stack.isEmpty() || node != null){
  9. while(node != null){
  10. // 首先拿到的是 根节点
  11. // 然后依次往下循环,获取到左节点,一直到最后一个左节点,这时栈里的节点都是左节点
  12. System.out.println(node.val);
  13. stack.push(node);
  14. node = node.left;
  15. }
  16. // 弹出栈里的左节点,然后依次获取右节点
  17. node = stack.pop();
  18. node = node.right;
  19. }
  20. }

上面一种的变种:

  1. void preorder(TreeNode root){
  2. Deque<TreeNode> stack = new LinkedList<>();
  3. while(root != null || !stack.isEmpty()){
  4. if(root != null){
  5. // 在这里获取到的 root 就是前序遍历需要的节点顺序
  6. System.out.println(root.val);
  7. stack.push(root);
  8. root = root.left;
  9. }else{
  10. TreeNode node = stack.pop();
  11. root = node.right;
  12. }
  13. }
  14. }


二叉树的中序遍历:

中序遍历:左节点 -> 根节点 -> 右节点

  1. void inorder(TreeNode root){
  2. Deque<TreeNode> stack = new LinkedList<>();
  3. while(!stack.isEmpty() || root != null){
  4. if(root != null){
  5. stack.push(root);
  6. root = root.left;
  7. }else{
  8. // 这里获取到的节点就是中序遍历的节点
  9. TreeNode node = stack.pop();
  10. System.out.println(node.val);
  11. root = node.right;
  12. }
  13. }
  14. }

上面中序遍历的变种:
下面的中序遍历,也是先沿着一条线访问并入栈左节点,当轮询到的左节点为空时,表明当前的一条线走完,这条线上除了最后一个的左节点,同时也是其它节点父节点,所以出栈后就有可能是根节点。根据中序遍历的特点,出栈的节点就可以当做是遍历的需要点节点。走完左节点这一条线后,然后根据栈中保存的左节点,查找它的右节点,然后把这个右节点当做根节点再重复上面的步骤。

  1. void inorder(TreeNode root){
  2. Deque<TreeNode> stack = new LinkedList<>();
  3. while(root != null || !stack.isEmpty()){
  4. while(root != null){
  5. stack.push(root);
  6. root = root.left;
  7. }
  8. TreeNode node = stack.pop();
  9. // 这里获取到的节点就是中序遍历的节点
  10. System.out.println(node.val);
  11. root = node.right;
  12. }
  13. }

二叉树的后序遍历:

二叉树的后序循环遍历,个人认为比较难。不过可以在上面的前序遍历中的第一种方式的基础上做一下改变,就可以的到后序遍历方式。

  1. void postorder(TreeNode root){
  2. Deque<Integer> res = new LinkedList<>();
  3. Deque<TreeNode> stack = new LinkedList<>();
  4. stack.push(root);
  5. while(!stack.isEmpty()){
  6. // 将这里获取的节点是压入栈,当循环结束后,出栈的顺序就是后序遍历的
  7. // 除根几点外,在入栈时,是先左节点入栈,后右节点入栈。
  8. // 那么出栈时就是 先右节点出栈,后左节点出栈。加上最先出栈的根节点。
  9. // 正常的出栈顺序就是 根节点 -> 右节点 -> 左节点
  10. // 所以就需要存入另一个栈时,在出栈是就是 左节点 -> 右节点 -> 根节点,即后序遍历
  11. TreeNode node = stack.pop();
  12. if(node == null){
  13. continue;
  14. }
  15. res.push(node.val);
  16. // 先左节点入栈,后右节点入栈
  17. if(node.left != null){
  18. stack.push(node.left);
  19. }
  20. if(node.right != null){
  21. stack.push(node.right);
  22. }
  23. }
  24. }

另一种后序遍历:

  1. void postorder(TreeNode root){
  2. if(root == null){
  3. return;
  4. }
  5. Deque<TreeNode> stack = new LinkedList<>();
  6. TreeNode prev = null;
  7. while(root != null || !stack.isEmpty()){
  8. // 沿一条线,将其中的左节点压入栈中
  9. while(root != null){
  10. stack.push(root);
  11. root = root.left;
  12. }
  13. // 取出栈中一个节点,查看其是否存在右节点,或者其右节点是否是上一个节点
  14. root = stack.pop();
  15. if(root.right == null || root.right == prev){
  16. ans.add(root.val);
  17. prev = root;
  18. root = null;
  19. }else{
  20. stack.push(root);
  21. root = root.right;
  22. }
  23. }
  24. }

Review/Share:

Android Studio 升级后的修改