Morris遍历
介绍
morris遍历是利用二叉树本身空闲出来的指针(n个节点的二叉树有n+1个指针空闲)来作为辅助变量完成二叉树的遍历的一种方法。Morris遍历法能以O(1)的空间复杂度和O(n)的时间复杂度实现二叉树的三种遍历,其中不使用栈或额外空间。
Morris中序遍历过程
记当前节点为root
, 设置一个predecessor
指针用来指向当前节点左子树的最右边节点(中序遍历的前驱节点)
如果root
无左孩子,root
向右移动
如果root
有左孩子,predecessor
指向root左子树的最右边节点
- 如果
predecessor
的right指针指向空,让其指向root,root向左移动
- 如果
predecessor
的right指针指向root,让其指向null
,root向右移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| var inorderTraversal = function(root) { let res = [] let cur = root let mosRight = null while(cur !== null) { if(cur.left !== null) { mosRight = cur.left while(mosRight.right !== null && mosRight.right !== cur) { mosRight = mosRight.right } if(mosRight.right === null) { mosRight.right = cur cur = cur.left } else { mosRight.right = null res.push(cur.val) cur = cur.right } } else { res.push(cur.val) cur = cur.right } } return res }
|
前序遍历
后序遍历
复杂度分析
对于二叉树来说,传统的遍历方式,一个节点最多只可能被访问两次,因此其时间复杂度均为O(n)
。而由于借助了栈和队列这样的辅助数据结构,无论哪种方式,其空间复杂度与树高有直接关系,因此传统遍历方式空间复杂度为平均O(logn)
,最差O(n)
而morris遍历对于每一个有左子树的节点,其寻找前驱的过程只会执行两次,一次是建立前驱-后继关系的时候,一次是恢复树结构的时候。因此事实上,二叉树的每条路最多只可能被循环访问两次,其时间复杂度必然为O(n)
,空间复杂度O(1)
给你二叉搜索树的根节点 root ,该树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。
进阶:使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用常数空间的解决方案吗?
示例 1:
输入:root = [1,3,null,null,2]
输出:[3,1,null,null,2]
解释:3 不能是 1 左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。
示例 2:
输入:root = [3,1,4,null,null,2]
输出:[2,1,4,null,null,3]
解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| var recoverTree = function(root) { let x = null, y = null, pred = null, predecessor = null while(root !== null) { if(root.left !== null) { predecessor = root.left while(predecessor.right !== null && predecessor.right !== root) { predecessor = predecessor.right } if(predecessor.right === null) { predecessor.right = root root = root.left } else { if(pred !== null && root.val < pred.val) { y = root if(x === null) { x = pred } } pred = root predecessor.right = null root = root.right } } else { if(pred !== null && root.val < pred.val) { y = root if(x === null) { x = pred } } pred = root root = root.right } } [x.val, y.val] = [y.val, x.val] }
|