一文掌握二叉树的各种遍历方式

133 阅读4分钟

二叉树的遍历方式

  • 二叉树有两种存储结构,一种是用数组存储,通过各结点下标的关系组成树,另外一种是链式存储,各节点之间以指针相连。
  • 对于数组存储的二叉树,设起始下标为1,对于下标为n的某个结点,其左孩子下标为2n,右孩子为2n+1,父节点为n/2。
  • 链式结构,下面的遍历都以链式结构实现
  • image-20230913163510946

递归序

  • 树的递归中,对于一个结点会访问到三次,第一次从父结点访问到,第二次从左结点访问到,第三次从右结点访问到。

遍历二叉树

  • 先序:头左右
  • 中序:左头右
  • 后序:左右头

递归

void process(Node head){
    if(head == null) return;
    // 在这里输出head就是先序
    process(head.left);
    // 在这里输出head就是中序
    process(head.right);
    // 在这里输出head就是后序
}

非递归

  • 使用栈进行遍历
    • 先序遍历

    • 先将头节点加入栈中,以栈不为空进行循环,每次循环弹出栈顶元素并打印,同时判断有无左右孩子,有则加入,先右后左。

    • image-20230913164931721

    • image-20230913165037723

  • 后序

    • 使用两个栈a,b,先将头节点加入a中,以a不为空为条件进行循环,每次循环弹出栈顶元素并加入b,同时判断有无左右孩子,有则加入a,先左后右。结束循环后依次弹出b中元素并打印。
    • image-20230913165933688
    • image-20230913170029446
  • 中序

    • 先将左边界的结点全部入栈,弹出的过程中看看是否存在右子树,存在则对右子树的左边界全部入栈,重复上述操作。
    • image-20230913170329157
    • image-20230913170818963

宽度优先遍历

  • 通过队列实现,先放左再放右,弹出就打印

Morris遍历

  • image-20230926155704817

  • 细节image-20230926155756755

  • 实现代码

      static void morris(Node head) {
            // cur记录所在结点,cur不为null则一直循环
            Node cur = head;
            while (cur != null) {
                System.out.print(cur.value);
                if (cur.left == null) {
                    // 1.如果没有左子树就cur就向右走
                    cur = cur.right;
                } else {
                    // 2.如果有左子树就找到左子树的最右结点mostRight
                    Node mostRight = cur.left;
                    while (mostRight.right != null && mostRight.right != cur) {
                        mostRight = mostRight.right;
                    }
                    if(mostRight.right == null){
                        // 2.1 如果mostRight的右指针为空则令右指针指向cur,cur向左走
                        mostRight.right = cur;
                        cur = cur.left;
                    }else {
                        // 2.2 如果mostRight的右指针不为空则让cur向右走,并令mostRight的右指针为空
                        mostRight.right = null;
                        cur = cur.right;
                    }
                }
            }
        }
    
  • morris序到先中后序

    • 先序:在第一次遍历到这个结点时就进行打印,第二次则不进行打印。
    • 如何判断是第一次还是第二次?如果是通过叶节点的指针遍历到的就是第二次!
    • 中序:对于可以遍历到第二次的就在第二次遍历时打印,不能的就在第一次遍历时打印
    • static void morris(Node head) {
              // cur记录所在结点,cur不为null则一直循环
              Node cur = head;
              while (cur != null) {
                  if (cur.left == null) {
                      // System.out.print(cur.value); 中序和先序都要
                      // 1.如果没有左子树就cur就向右走
                      cur = cur.right;
                  } else {
      ​
                      // 2.如果有左子树就找到左子树的最右结点mostRight
                      Node mostRight = cur.left;
                      while (mostRight.right != null && mostRight.right != cur) {
                          mostRight = mostRight.right;
                      }
                      if(mostRight.right == null){
                          // 2.1 如果mostRight的右指针为空则令右指针指向cur,cur向左走
                          mostRight.right = cur;
                          // System.out.print(cur.value); 先序
                          cur = cur.left;
                      }else {
                          // 2.2 如果mostRight的右指针不为空则让cur向右走,并令mostRight的右指针为空
                          mostRight.right = null;
                          // System.out.print(cur.value); 中序
                          cur = cur.right;
                      }
                  }
              }
          }
      
    • 后序,第二次来到同一个结点时逆序打印左树的右边界,最后打印整颗树的右边界image-20230926162755307
    •  static void morris(Node head) {
              // cur记录所在结点,cur不为null则一直循环
              Node cur = head;
              while (cur != null) {
                  if (cur.left == null) {
                      // 1.如果没有左子树就cur就向右走
                      cur = cur.right;
                  } else {
      ​
                      // 2.如果有左子树就找到左子树的最右结点mostRight
                      Node mostRight = cur.left;
                      while (mostRight.right != null && mostRight.right != cur) {
                          mostRight = mostRight.right;
                      }
                      if(mostRight.right == null){
                          // 2.1 如果mostRight的右指针为空则令右指针指向cur,cur向左走
                          mostRight.right = cur;
                          cur = cur.left;
                      }else {
                          // 2.2 如果mostRight的右指针不为空则让cur向右走,并令mostRight的右指针为空
                          mostRight.right = null;
                          printEdge(cur.left);
                          cur = cur.right;
                      }
                  }
              }
              printEdge(head);
          }
      ​
          static void printEdge(Node head){
              Node node = invertLink(head);
              head = node;
              while (node != null){
                  System.out.print(node.value);
                  node = node.right;
              }
              invertLink(head);
          }
      ​
          static Node invert(Node head) {
              Node help = null;
              Node temp = null;
              while (head != null) {
                  temp = head.right;
                  head.right = help;
                  help = head;
                  head = temp;
              }
              return help;
          }
      
  • 如何选择Morris和递归

    • 当需要第三次汇总信息强整合用递归最优解,其他则是morris最优