This commit is contained in:
chefyuan 2021-05-21 18:34:13 +08:00
parent 696c5bdfd1
commit 86ba473ebc
2 changed files with 103 additions and 4 deletions

View File

@ -26,7 +26,7 @@
我们都知道栈的特性是先进后出我们借助栈来帮助我们完成前序遍历的时候
则需要注意的一点是我们应该先将右子节点入栈再将左子节点入栈
则需要注意的一点是我们应该`先将右子节点入栈再将左子节点入栈`
这样出栈时则会先出左节点再出右子节点则能够完成树的前序遍历
@ -34,7 +34,7 @@
![](https://img-blog.csdnimg.cn/20210512205822221.gif)
我们用一句话对上图进行总结当栈不为空时栈顶元素出栈如果其右孩子不为空则右孩子入栈其左孩子不为空则左孩子入栈还有一点需要注意的是我们和层序遍历一样需要先将 root 节点进行入栈然后再执行 while 循环
我们用一句话对上图进行总结`当栈不为空时栈顶元素出栈如果其右孩子不为空则右孩子入栈其左孩子不为空则左孩子入栈`还有一点需要注意的是我们和层序遍历一样需要先将 root 节点进行入栈然后再执行 while 循环
看到这里你已经能够自己编写出代码了不信你去试试
@ -69,7 +69,105 @@ class Solution {
### Morris
Morris 遍历利用树的左右孩子为空大量空闲指针实现空间开销的极限缩减这个遍历方法稍微难理解一些不过结合动图也就一目了然啦各位系好安全带我们要发车啦
Morris 遍历利用树的左右孩子为空大量空闲指针实现空间开销的极限缩减这个遍历方法稍微有那么一丢丢难理解不过结合动图也就一目了然啦下面我们先看动画吧
看完视频是不是感觉自己搞懂了又感觉自己没搞懂哈哈咱们继续往下看
![image](https://cdn.jsdelivr.net/gh/tan45du/test@master/image.1u3at0ckvn34.png)
我们之前说的Morris 遍历利用了`树中大量空闲指针的特性`我们需要`找到当前节点的左子树中的最右边的叶子节点`将该叶子节点的 right 指向当前节点例如当前节点为2其左子树中的最右节点为 9 则在 9 节点添加一个 right 指针指向 2
其实上图中的 Morris 遍历遵循两个原则我们在动画中也能够得出
1. p1.left == null p1 = p1.right(这也就是我们为什么要给叶子节点添加 right 指针的原因)
2. 如果 p1.left != null找到 p1 左子树上最右的节点(也就是我们的 p2 最后停留的位置)此时我们又可以分为两种情况一种是叶子节点添加 right 指针的情况一种是去除叶子节点 right 指针的情况
3. - 如果 p2 right 指针指向空让其指向 p1p1向左移动, p1 = p1.left
- 如果 p2 right 指针指向 p1让其指向空为了防止重复执行则需要去掉 right 指针p1 向右移动p1 = p1.right
这时你可以结合咱们刚才提到的两个原则再去看一遍动画并代入规则进行模拟差不多就能完全搞懂啦
下面我们来对动画中的内容进行拆解
首先 p1 指向 root节点
p2 = p1.left下面我们需要通过 p2 找到 p1的左子树中的最右节点即节点 5然后将该节点的 right 指针指向 root并记录 root 节点的值
![image](https://cdn.jsdelivr.net/gh/tan45du/test@master/image.3h60vcjhqo80.png)
向左移动 p1 p1 = p1.left
p2 = p1.left 即节点 4 找到 p1 的左子树中的最右叶子节点也就是 9并将该节点的 right 指针指向 2
![image](https://cdn.jsdelivr.net/gh/tan45du/test@master/image.zq91mdjkyzk.png)
继续向左移动 p1, p1 = p1.leftp2 = p1.left 也就是节点 8并将该节点的 right 指针指向 p1
![image](https://cdn.jsdelivr.net/gh/tan45du/test@master/image.5vsh71yrzxs0.png)
我们发现这一步给前两步是一样的都是找到叶子节点将其 right 指针指向 p1,此时我们完成了添加 right 指针的过程下面我们继续往下看
我们继续移动 p1 指针p1 = p1.leftp2 = p.left此时我们发现 p2 == null,即下图
![image](https://cdn.jsdelivr.net/gh/tan45du/test@master/image.zk7nxrjdgr.png)
此时我们需要移动 p1, 但是不再是 p1 = p1.left 而是 p1 = p1.right也就是 4继续让 p2 = p1.left此时则为下图这种情况
![image](https://cdn.jsdelivr.net/gh/tan45du/test@master/image.1pjni9r6tkps.png)
此时我们发现 p2.right != null 而是指向 4说明此时我们已经添加过了 right 指针所以去掉 right 指针并让 p1 = p1.right
![image](https://cdn.jsdelivr.net/gh/tan45du/test@master/image.17t7n8yy340w.png)
下面则继续移动 p1 ,按照规则继续移动即可遇到的情况已经在上面做出了举例所以下面我们就不继续赘述啦如果还不是特别理解的同学可以再去看一遍动画加深下印象
时间复杂度 On空间复杂度 O1
下面我们来看代码吧
#### 代码
```java
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if (root == null) {
return list;
}
TreeNode p1 = root; TreeNode p2 = null;
while (p1 != null) {
p2 = p1.left;
if (p2 != null) {
//找到左子树的最右叶子节点
while (p2.right != null && p2.right != p1) {
p2 = p2.right;
}
//添加 right 指针对应 right 指针为 null 的情况
if (p2.right == null) {
list.add(p1.val);
p2.right = p1;
p1 = p1.left;
continue;
}
//对应 right 指针存在的情况则去掉 right 指针
p2.right = null;
} else {
list.add(p1.val);
}
//移动 p1
p1 = p1.right;
}
return list;
}
}
```
好啦今天就看到这里吧咱们下期见

View File

@ -64,4 +64,5 @@ class Solution {
我们根据代码可知我们只会移动比 temp 值大的元素所以我们排序后可以保证相同元素的相对位置不变所以直接插入排序为稳定性排序算法
![](https://cdn.jsdelivr.net/gh/tan45du/bedphoto2@master/20210122/微信截图_20210128084750.6911k6mnrac0.png)
![](https://cdn.jsdelivr.net/gh/tan45du/bedphoto2@master/20210122/微信截图_20210128084750.6911k6mnrac0.png)