非递归遍历二叉树

前言

因为树的定义本身就是递归定义,所以采用递归的方法遍历树不仅容易理解而且代码很简洁。
遍历二叉树的三种方式,若采用递归方法,思路非常简洁,代码几乎不用作更改。
但若要采用非递归的方法,则可以采用栈去模拟实现。三种遍历方式相同之处是都使用到栈,但思路相差很大,
理解难度顺序为: 前序 < 中序 < 后序


三种非递归的遍历算法及分析

不用递归方法先序遍历二叉搜索树
分析:先序遍历最好理解,每次取出栈顶元素后,先输出自己,再将右子和左子压入栈,无需多言。

void preorder(Node *head){
	stack<Node*>nstk;
	nstk.push(head);
	while (!nstk.empty()){
		Node *temp = nstk.top();
		nstk.pop();
		cout << temp->val << " ";
		if(temp->rs) nstk.push(temp->rs);
		if(temp->ls) nstk.push(temp->ls);
	}
	cout << endl;
	return;
}

.

不用递归的中序遍历方法
分析:想比先序遍历稍稍复杂了一点,分析其核心规则仅有两点:

  • 将一个节点压入栈后要保证其左子随后压入,直到没有左子。
  • 入栈步骤完成后取出栈顶,输出值,随后将右子压入栈(若有右子)。

在第一点中,入栈顺序保证了自己输出时左孩子全部都已经输出了。
第二点则保证了全部右儿子在自己输出之后输出,这样刚好满足了
中序遍历的规律,不难理解。

void inorder(Node *head){
	stack<Node *> nstk;
	Node *p = head;
	while (p != NULL || !nstk.empty()){
		while (p != NULL){
			nstk.push(p);
			p = p->ls;
		}
		if (!nstk.empty()){
			p = nstk.top();
			cout << p->val << " ";
			nstk.pop();
			p = p->rs;
		}
	}
}

.
不用递归后序遍历方法
分析: 与前两个算法比较代码长度差不多,但是需要增加一个变量 read 来判断当前节点是
否已经输出。总结规则如下:

  • 仅当自己的全部儿子都输出了(若有)才输出自己,否则将儿子都压到栈顶(右子优先)
  • 不断取栈顶元素进行操作,直至栈为空

注意为了不全部写到一行,这里用到了宏定义来写判断当前节点是否可以输出自己的
方法 SHOULD ( p ), 还有判断一个节点是否已经输出的方法 ISREAD( p )。
后者目的是避免读取NULL值。

#define ISREAD(p) (p==NULL?true:p->read)
#define  SOULD(p) ( (!p->ls && !p->rs) || (ISREAD(p->ls) && ISREAD(p->rs)) )
void postorder(Node *head){
	stack<Node*> nstk;
	nstk.push(head);
	while (!nstk.empty()){
		Node *p = nstk.top();
		if (SOULD(p)){
			cout << p->val << " ";
			p->read = true;
			nstk.pop();
		}
		else{
			if (p->rs) nstk.push(p->rs);
			if (p->ls) nstk.push(p->ls);
		}
	}
}

测试代码

#include<iostream>
#include<stdio.h>
#include<queue>
#include<stack>
#define ISREAD(p) (p==NULL?true:p->read)
#define  SOULD(p) ( (!p->ls && !p->rs) || (ISREAD(p->ls) && ISREAD(p->rs)) )
using namespace std;

//define a node of tree
struct Node{
	int val;
	Node *ls, *rs;
	bool read;	//use for judge if this node have been ouput. only used by postorder()
	Node(int n){ ls = rs = NULL, val = n, read=false; }
};
//注意这里构建的是二叉搜索树
//build binary search tree
void Insert(Node *&n, int v){	//note that you have to use pointer reference!
	if (n == NULL){
		n = new Node(v);
		return;
	}
	if (v < n->val) Insert(n->ls, v);
	else Insert(n->rs, v);
}
//display the tree by preorder,purpose only for test
void display(Node* n){
	if (n == NULL) return;
	cout << n->val << " ";
	display(n->ls);
	display(n->rs);
}

//逐层从左到右遍历二叉搜索树,广度优先搜索方式变量
void bfs(Node *head){
	if (head == NULL) return;
	queue<Node*> nque;
	nque.push(head);
	while (!nque.empty()){
		Node *temp = nque.front();
		nque.pop();
		cout << temp->val << " ";
		nque.push(temp->ls);
		nque.push(temp->rs);
	}
	cout << endl;
	return;
}
//非递归先序遍历二叉搜索树
void preorder(Node *head){
	stack<Node*>nstk;
	nstk.push(head);
	while (!nstk.empty()){
		Node *temp = nstk.top();
		nstk.pop();
		cout << temp->val << " ";
		if(temp->rs) nstk.push(temp->rs);
		if(temp->ls) nstk.push(temp->ls);
	}
	cout << endl;
	return;
}
//非递归中序遍历方法
void inorder(Node *head){
	stack<Node *> nstk;
	Node *p = head;
	while (p != NULL || !nstk.empty()){
		while (p != NULL){
			nstk.push(p);
			p = p->ls;
		}
		if (!nstk.empty()){
			p = nstk.top();
			cout << p->val << " ";
			nstk.pop();
			p = p->rs;
		}
	}
	cout<<endl;
}
//非递归后序遍历方法
void postorder(Node *head){
	stack<Node*> nstk;
	nstk.push(head);
	while (!nstk.empty()){
		Node *p = nstk.top();
		if (SOULD(p)){
			cout << p->val << " ";
			p->read = true;
			nstk.pop();
		}
		else{
			if (p->rs) nstk.push(p->rs);
			if (p->ls) nstk.push(p->ls);
		}
	}
	cout<<endl;
}



int main(){
	//get data and build the tree
	int n;
	Node *head = NULL;
	cin >> n;
	for (int i = 0; i < n; i++){
		int t;
		cin >> t;
		Insert(head, t);
	}
	//test the method
	cout << "preorder(head);" << endl;
	preorder(head);
	cout << "inorder(head);" << endl;
	inorder(head);
	cout << "postorder(head);" << endl;
	postorder(head);
}
/*
mock input data
15
8 4 2 1 3 6 5 7 12 10 9 11 14 13 15
*/

测试数据

15
8 4 2 1 3 6 5 7 12 10 9 11 14 13 15
树的形状:
在这里插入图片描述
输出结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值