茴字有四种写法, 二叉树有三种遍历手段(递归, 栈, morris遍历)
先序遍历
- root进栈
- 栈非空:
- 栈顶元素(父节点)出栈并处理
- 右子节点, 左子节点进栈, 这样下一轮会先处理左子节点, 符合父->左->右的顺序
|
|
后序遍历
为了按后序遍历的顺序处理每个节点, 在考察每个节点时需要先判断前一轮循环处理的节点, 用变量last_pop
维护.
- 是否处理完左右子树? 通过查看
last_pop
是否为当前节点的子节点判断. 因为如果已经处理了左/右子树, 他们都已出栈, 现在栈顶的元素会是他们的父节点. (左右皆可能, 因为不一定有右子节点, 故last_pop
也可能是左子节点)- 若是, 处理当前节点. 出栈并打印, 更新
last_pop
- 若不是, 先处理左右子树, 即按右/左顺序压栈, 下一轮循环就会先处理左子节点(栈顶)
- 若是, 处理当前节点. 出栈并打印, 更新
- 是否到达叶子节点? 若到达, 出栈并打印, 更新
last_pop
变量解释与实现:
cur
: 当前遍历指针, 初始状态指向root
last_pop
: 上次处理的节点
finishedSubtrees
: 是否处理完左右子树
isLeaf
: 是否到达叶子节点
|
|
例子
|
|
last_pop | cur | finishedSubtrees | isLeaf | op | stack |
---|---|---|---|---|---|
0 | 0 | F | F | init | 0 |
0 | 0 | F | F | br2: push 2,1 | 021 |
0 | 1 | F | F | br2: push 3 | 0213 |
0 | 3 | F | T | br1: pop 3 | 021 |
3 | 1 | T | F | br1: pop 1 | 02 |
1 | 2 | F | F | br2: push 5,4 | 0254 |
1 | 4 | F | T | br1: pop 4 | 025 |
4 | 5 | F | T | br1: pop 5 | 02 |
5 | 2 | T | F | br1: pop 2 | 0 |
2 | 0 | T | F | br1: pop 0 |
br
代表进入了哪个判断分支.
Result: 3, 1, 4, 5, 2, 0
中序遍历
同后序遍历, 只不过处理顺序需要稍微改一改, 遍历(压栈)顺序依然维持不变
- 是否处理完左子树? 通过查看
last_pop
是否为当前节点的左子节点判断. 若无左子节点也一并视为已处理完- 若是, 处理当前节点. 出栈并打印, 更新
last_pop
, 并将右子节点压栈, 等待下一轮处理 - 若不是, 先处理左子树, 即左节点压栈, 等待下一轮处理
- 若是, 处理当前节点. 出栈并打印, 更新
- 是否到达叶子节点? 若到达, 出栈并打印, 更新
last_pop
变量解释与实现
cur
: 当前遍历指针, 初始状态指向root
last_pop
: 上次处理的节点
finishedLeftTrees
: 是否处理完左子树
isLeaf
: 是否到达叶子节点
|
|
线索树的构造
利用已存在的空节点保存前驱/后继节点, 这方面的文章很多, 中文资料参考 https://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html