morris遍历

在这里插入图片描述
二叉树的遍历方式,无论是递归还是非递归的,都绕不过一个东西,叫额外空间复杂度O(H) (其中 H 为树高)。
因为二叉树遍历过程中只能向下查找孩子节点而无法回溯父结点,因此这些算法借助栈来保存要回溯的父节点(递归的实质是系统帮我们压栈,非递归的方式是 我们自己压栈),并且栈要保证至少能容纳下 H 个元素(比如遍历到叶子结点时回溯父节点,要保证其所有父节点在栈中)。
而morris遍历则能做到时间复杂度仍为 O(N) 的情况下额外空间复杂度只需 O(1) 。

morris遍历利用了二叉树 大量的空闲的空间。
比如像下面这种,
在这里插入图片描述
有四个空节点完全没用!
在这里插入图片描述
在这里插入图片描述
例子:
先忘掉 先序,后序,中序
只有Morris 遍历。
在这里插入图片描述
cur 的左孩子不为空,说明 cur 的左子树存在,找出该左子树的最右结点,记为 mostRight
mostRight 的右孩子为空,那就让其指向 cur ( mostRight.right=cur ),并左移cur ( cur=cur.left )
在这里插入图片描述
在这里插入图片描述
同理,此时,cur左子树不为空,找出该左子树的最右结点,记为 mostRight
如果, mostRight 的右孩子为空,那就让其指向 cur ( mostRight.right=cur ),并左移
cur ( cur=cur.left )
在这里插入图片描述
此时,cur划过的 节点为:
在这里插入图片描述
此时,cur 的左孩子为空,说明 cur 的左子树不存在,那么 cur 右移来到 cur.right,即回到2。
在这里插入图片描述
此时,cur 的左孩子不为空
cur的左子树,是4,4右指针是指向cur的!即4右指针 不空。即cur左子树的最右结点mostRight是指向cur的。
所以,mostRight 的右孩子不空
那么让 cur 右移( cur=cur.right ),并将 mostRight 的右孩子置空。
在这里插入图片描述
此时,cur没有左孩子,所以说明 cur 的左子树不存在,那么 cur 右移来到 cur.right
在这里插入图片描述
此时,
cur 的左孩子不为空,说明 cur 的左子树存在,找出该左子树的最右结点,记为 mostRight。
mostRight 的右孩子为cur,不为空,那么让 cur 右移( cur=cur.right ),并将 mostRight 的右
孩子置空。
在这里插入图片描述
此时 cur 的左孩子不为空,即cur的左子树存在,将该左子树的最右结点6记为 mostRight

mostRight 6的右孩子为空,那就让其指向 cur ( mostRight.right=cur ),并左移cur( cur=cur.left )
得到:
在这里插入图片描述
此时cur 的左孩子为空,cur 的左子树不存在,cur 右移来到3
在这里插入图片描述
cur 的左孩子不为空,该左子树的最右结点6,记为 mostRight
mostRight 的右孩子=cur,不为空,那么让 cur 右移( cur=cur.right ),并将 mostRight 的右
孩子置空
在这里插入图片描述
cur没有左孩子,直接向右移动,划向 空
整个遍历过程停止,
cur 划过的点有:
在这里插入图片描述

代码:

在这里插入图片描述
蓝色部分指 我来到一个当前节点,当其没有左孩子的时候,这部分是不会发生的。
在这里插入图片描述
之后cur向右移动。
在这里插入图片描述
这段代码不断地 寻找左子树上 最右节点。
在这里插入图片描述
如果mostright不等于空,且不等于当前节点cur,执行while
这两个条件,只有两个都成立,才能往右。
有一个不成立,不能往右。

如果左子树最右节点为空
则满足此条逻辑,
在这里插入图片描述
相应代码:
在这里插入图片描述

接回讲morris

只要有一个节点,存在左子树,那么cur返回这个节点两次;且第二次到达该节点时,左子树都已经遍历完了。
不存在左子树,那么cur返回这个节点一次
在这里插入图片描述
利用左子树 最右节点的 右指针 指向谁 这件事 来标记 是第一次来到这个节点 还是第二次。

如果左子树 最右节点的 右指针 指向null,那么就是第一次来。
如果左子树 最右节点的 右指针 指向 自身,那么就是第二次来。

不是完全二叉树的情况,也可以用morris遍历
在这里插入图片描述
进阶3-2
在这里插入图片描述
morrispre 改先序的code:
在这里插入图片描述
打印行为在这里:
在这里插入图片描述
一个当前节点没有左子树的时候 会 执行 else
这部分在 进阶3-2,不是很懂。

后序序列

使用morris遍历得到二叉树的后序序列就没那么容易了,因为对于树种的非叶结点,morris遍历最多会经过它两次,而我们后序遍历实在第三次来到该结点时打印该结点的。因此要想得到后序序列,仅仅改变在morris遍历时打印结点的时机是无法做到的。

但其实,在morris遍历过程中,如果在每次遇到第二次经过的结点时,将该结点的左子树的右边界上的结点从下到
上打印,最后再将整个树的右边界从下到上打印,最终就是这个数的后序序列:

后序遍历中,我们关注的是,能够回到自身两次的节点。
例子:
在这里插入图片描述
一个节点必须两次回到自己,

morris遍历:
在这里插入图片描述
删除morris遍历 只回到一次的 节点
在这里插入图片描述
只关注morris遍历 中 能够 来到两次的节点。
当第二次来到2的时候,逆序打印 其 左子树的右边界。即4
当第二次来到1的时候,逆序打印 其 左子树的右边界。即5,2
当第二次来到3的时候,逆序打印 其 左子树的右边界。即6
然后函数退出之前,单独打印整棵树的右边界:7,3,1

最后结果为:
在这里插入图片描述
相应代码:
在这里插入图片描述
如果一个节点可以回到两次,执行if
在这里插入图片描述
发现是第二次来的时候,执行else
在这里插入图片描述
逆序打印左子树右边界
在这里插入图片描述
等while跑完后,
单独 逆序打印整棵树的右边界
在这里插入图片描述
那么这么样 在额外空间复杂度为O(1)的前提下,实现 逆序打印树的右边界?
肯定不能用栈,不然复杂度超过了。

第二次来到a的时候,拓扑结构是这样的。
左子树最右节点 的指针 是 指向 我自己的在这里插入图片描述
用链表reverse的方法 把指针给改了。
在这里插入图片描述
讲edcb打印完以后,
把指针都调回来。
完成逆序打印。
在这里插入图片描述
进阶3-3 01.51 讲时间复杂度,不是很懂,之后再听一遍吧。

整棵树是可以被右节点分解掉的;
所有右边界的节点个数是N,而每个右节点只会遍历有限几次,所以时间复杂度O(N)
在这里插入图片描述
例子:
这一棵树,
在这里插入图片描述
mirrors遍历:
在这里插入图片描述
整颗左子树都可以被右边界分解:
在这里插入图片描述
且每个右边界都可以趟两遍:

总结:
整棵树 整体 可以被右边界分解:
所以右边界 整体 个数为N,
而每个右边界最多只能趟 有限几次。
所以复杂度为O(N)
在这里插入图片描述
总结:
以后用遍历解决的问题,都可以用 morris遍历解决。
morris遍历是极为强大的装逼利器,面试官一旦问到遍历,就往这个方向上靠!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值