二叉树的前序遍历-mirrors大法

360 阅读2分钟

二叉树的前序遍历-mirrors遍历

今天刷leetcode发现二叉树的前序遍历不只有递归和利用栈\队列迭代的两种算法,于是这篇文章就诞生了。

mirrors来源

该方法由 J. H. Morris 在 1979 年的论文「Traversing Binary Trees Simply and Cheaply」中首次提出,因此被称为 Morris 遍历。

痛点

前序遍历的思路就是先访问root,再访问left,再访问right,但是如果使用常规遍历的话,在访问完left后,怎么回到root或者说访问到right呢?如何找到”回家的路“呢?

完整代码(JavaScript版本)

/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
*     this.val = (val===undefined ? 0 : val)
*     this.left = (left===undefined ? null : left)
*     this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root) {
   const result = [];
   let p = root;
   let q = null;
   while(p) {
       q = p.left;
       if(q === null){
           result.push(p.val);
           p = p.right;
       }else {
           while(q.right!==null && q.right !== p) {
               // 这个while循环就是寻找最右边的节点
               q = q.right;
           }
           // 寻找q.right是否连接着p
           if(q.right === null) {
               // 否
               q.right = p;
               result.push(p.val);
               p = p.left;
           }else if(q.right === p) {
               // 是
               q.right = null;
               p = p.right;
           }
       }
   }
   return result;
};

思路与算法

在访问的每一个节点p,以及它的左节点q( q = p.left)

  • 1、如果q为空:比较简单,访问完p(result.push(p.val))后,访问p的右节点即可。(这里要么访问p的右节点,要么就是条“回家的路
  • 2、如果q不为空:
    • 快速找出q子树中最右边的节点(q = q.right; q = q.right ......)
      • 【没发现“回家的路”】如果发现这个点未连接p,就将最后一个节点去连接p啦(q.right = p)(“回家的路”就打通了),顺便把访问到的p(result.push(p.val))加入结果中,这时候就可以放心的访问p的左节点了(p = p.left)
      • 【发现“回家的路”】如果发现这个点已经连接着p,那就说明p的左子树已经访问完了,这时候去访问右子树就行(p = p.right),顺便把这条“回家的路”给删掉(q.right = null)

复杂度分析

时间复杂度:O(n),其中 n 是二叉树的节点数。没有左子树的节点只被访问一次,有左子树的节点被访问两次。

空间复杂度:O(1)。只操作已经存在的指针(树的空闲指针),因此只需要常数的额外空间。

拓展

中序遍历、后续遍历的mirrors方法都是类似的,只是取val的时机和方法不同而已,整体思路是一样的。

结束语

遇到新鲜的东西就学习记录一下,还是挺有意思的。如果有描述的不对的地方欢迎指出,感谢阅读!

参考

leetcode-cn.com/problems/bi…