清华大学 邓俊辉 数据结构 学习笔记
遍历
V 父节点
L 左子树
R 右子树
先序遍历:V->L->R
中序遍历:L->V->R
后序遍历:L->R->V
层次(广度):自上而下,先左后右
先序遍历
递归实现
1 2 3 4 5 6 7 8
| template <typename T, typename VST> void traverse(BinNodePosi(T) x, VST & visit) { if (!x) return; visit (x->data); traverse(x->lchild, visit); traverse(x->rchild, visit); }
|
由于递归的实现机制,并不能做到每次递归的帧都足够小
迭代实现
1 2 3 4 5 6 7 8 9 10 11 12
| template <typename T, typename VST> void travPre_I1(BinNodePosi(T) x, VST& visit) { stack <BinNodePosi(T)> s; if (x) S.push(x); while (!s.empty()) { x = S.pop(); visit(x->data); if (HasRChild(*)) S.push(x->rChild); if (HasLChild(*)) S.push(x->lchild); } }
|
新算法
宏观过程:自顶向下依次遍历左子树,自底向上依次遍历右子树
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
| template <typename T, typename VST> static void visitAlongLeftBranch( BinNodePosi(T) x, VST & visit, stack <BinNodePosi(T)> &S) { while (x) { visit(x->data); S.push(x->rChild); x = x->lChild; } }
template <typename T, typename VST> void travPre_I2(BinNodePosi(T) x, VST & visit) { stack <BinNodePosi(T)> S; while (true) { visitAlongLeftBranch(x, visit, S); if (S.empty()) break; x = S.pop(); } }
|
中序遍历
递归
1 2 3 4 5 6 7 8
| tmeplate <typename T, typename VST> void traverse(BinNodePosi(T) x, VST & visit) { if (!x) return; traverse(x->lChild, visit); visit(x->data); traberse(x->rChild, visit); }
|
迭代
从根节点开始沿左侧分支往下找到第一个访问的节点。
从根节点开始,访问左孩子和左孩子的右子树,再访问根节点,访问根节点的右子树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| template <typename T> static void goAlongLetBranch( BinNodePosi(T) x, Stack <BinNodePosi(T)> & s) { while (x) { s.push(x); x = x->lChild; } } template <typename T, typename V> void travIn_I1( BinNodePosi(T) x, V& visit) { Stack <BinNodePosi(T)> S; while (true) { goAlongLeftBranch (x, s); if (s.empty()) break; x = S.pop(); visit (x->data); x = x->rchild; } }
|
分摊分析: O(n)
虽然和递归都是 O(n),但是从常系数上来看,递归的常系数更大,因此迭代更好。
后序遍历
将先序遍历的顺序改为V-R-L,倒置后即可。参考Leetcode-145题(参考代码)
层次遍历
自高向低,每一层自左向右访问节点
1 2 3 4 5 6 7 8 9 10 11 12 13
| template <typename T> template <typename VST> void BinNode<T>::travLevel( VST & visit) { Queue<BinNodePosi(T)> Q; Q.enqueue(this); while (!Q.empty()) { BinNodePosi(T) x = Q.dequeue(); visit (x->data); if ( HasLChild(*x)) Q.enqueue(x->lChild); if ( HasRChild(*x)) Q.enqueue(x->rChild); } }
|
重构
已知某棵树的遍历序列,还原这棵树的拓扑结构
[先序 | 后续] + 中序
先序遍历确认根节点,在中序遍历中找到根节点,且分开左右子树,将整个二叉树的重构问题转化为左右子树的重构问题。注意左、右子树可能是空树。
[先序 + 后续] X 真二叉树
左右子树要么同时为空,要么非空
先序遍历:根节点+左子树根节点引领的左子树遍历子序列+右子树根节点引领的右子树遍历子序列
后续遍历:以左子树根节点结尾的左子树遍历子序列+右子树根节点结尾的右子树遍历子序列+根节点
可以明确界定左右子树的范围