准备
我们在做的事情有另一个说法——用循环(Loop)替代递归(Recursion)完成迭代(Iteration)。
循环的本质是什么?——单路递归
循环的本质其实就是单路递归。
比如顺序遍历数组的过程:
即使不需要启用递归也能写出,因为循环本身就是单路递归。
for(int i = 1; i <= n; ++i) {
visit(myvector + i);
}
简单的双路递归
比如逆序遍历链表的过程:
这样的双路递归可以很方便的用==循环加一个栈==写出来。
stack<mylistnode*> stk;
for(; cur->next; cur = cur->next) {
stk.push(cur);
}
while(!stk.empty()) {
visit(stk.top());
stk.pop();
}
我们从中抽象出一般的转换方法:
循环1路,入栈2路
先序遍历
二叉树的先序遍历是这样的:
二叉树的先序遍历是一个简单的三路递归,但visit函数是在开头一个常数级操作,所以二叉树的先序遍历其实是可以用二路递归来写:
我们套用公式就可以无脑写代码了,循环1路(visit + fleft),入栈2路(fright)
//当然栈是必不可少的
stack<TreeNode*> stk;
TreeNode* cur = root;
//while条件很多友友不知道怎么写,其实就是你研究的f的结束条件
while(cur || !stk.empty()) {
//visit + fleft
while(cur) {
//访根入右移动左,口诀可以背诵
visit(cur);
stk.push(cur->rchild);
cur = cur->lchild;
}
//fright
//出栈右
cur = stk.top();
stk.pop();
}
中序遍历
中序遍历同样十分简单,直接公式法:
循环1路(fleft),入栈2路(visit + fright)
代码基本和先序遍历没啥差别:
stack<TreeNode*> stk;
TreeNode* cur = root;
//while条件很多友友不知道怎么写,其实就是你研究的f的结束条件
while(cur || !stk.empty()) {
//fleft
while(cur) {
//入栈根右移动左,口诀可以背诵
stk.push(cur);
cur = cur->lchild;
}
//visit + fright
//出栈根右
cur = stk.top();
stk.pop();
visit(cur);
cur = cur->rchild;
}
有友友看到这里可能要说了:就这!好简单呐!别着急,重头戏来了——
后序遍历
如果直接上公式——
循环1路(fleft),入栈2路(fright + visit)
这回我们发现,一个栈可存不下二路这两个函数了:
所以拆包2路现在有两个办法:
- 维护法:通过维护一个指针lastvisited来判断右边节点是否已经被访问来决定是否调用visit()
stack<TreeNode*> stk; TreeNode* cur = root, * lastvisited = nullptr; //while条件很多友友不知道怎么写,其实就是你研究的f的结束条件 while(cur || !stk.empty()) { //fleft while(cur) { //入栈右根移动左,口诀可以背诵 stk.push(cur); cur = cur->lchild; } //fright + visit //出栈右根 TreeNode* topnode = stk.top(); //注意这里千万不能stk.pop();,因为根节点还没访问 if(nullptr != topnode->rchild && lastvisited != topnode->rchild){ cur = topnode->rchild; } else { visit(topnode); lastvisited = topnode; stk.pop(); } }
- 倒置法:倘若将左右根倒置为根右左,那就只需要按照伪二路递归的先序遍历来做就行.新公式:stack倒置(循环1路(visit + fright),入栈2路(fleft))
stack<TreeNode*> s1, s2; TreeNode* cur = root; //while条件很多友友不知道怎么写,其实就是你研究的f的结束条件 while(cur || !s1.empty()) { visit + fright while(cur) { //访根入左移动右,口诀可以背诵 s2.push(cur); s1.push(cur->lchild); cur = cur->rchild; } fleft //出栈左 cur = s1.top(); s1.pop(); } //通过s2复原左右根 while(!s2.empty()) { visit(s2.top()); s2.pop(); }
三叉树?
让我们来一点大胆的想法——若定义了三叉树的先序遍历,那么也可以有公式——
循环1路(visit + fleft),入栈2路(fmid + fright)
示例代码:
stack<TreeNode*> stk;
TreeNode* cur = root;
while(cur || !stk.empty()) {
//v + l
while(cur) {
visit(cur);
//注意这里一定是先rchild入栈再是mchild
stk.push(cur->rchild);
stk.push(cur->mchild);
cur = cur->lchild;
}
//m + r
cur = stk.top();
stk.pop();
}
简单的四路递归就完成了!