【2023-05-20】怎样优雅地写出一棵二叉树的非递归遍历

准备

我们在做的事情有另一个说法——用循环(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路现在有两个办法:

  1. 维护法:通过维护一个指针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();
    	}
    }
    
    
  2. 倒置法:倘若将左右根倒置为根右左,那就只需要按照伪二路递归的先序遍历来做就行.新公式: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();
}

简单的四路递归就完成了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值