前言
因为树的定义本身就是递归定义,所以采用递归的方法遍历树不仅容易理解而且代码很简洁。
遍历二叉树的三种方式,若采用递归方法,思路非常简洁,代码几乎不用作更改。
但若要采用非递归的方法,则可以采用栈去模拟实现。三种遍历方式相同之处是都使用到栈,但思路相差很大,
理解难度顺序为: 前序 < 中序 < 后序
三种非递归的遍历算法及分析
不用递归方法先序遍历二叉搜索树
分析:先序遍历最好理解,每次取出栈顶元素后,先输出自己,再将右子和左子压入栈,无需多言。
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
树的形状:
输出结果: