二叉树主要有三种遍历方式:前序遍历、中序遍历和后序遍历,每种方式都有递归和非递归两种方法。递归的方法简单明了,但是会比较耗时,非递归的方法效率相对较高,但是算法也比较晦涩。本文就这三种遍历方式做简单的介绍。
数据结构:
struct BTree{
int data;
BTree *lchild;
BTree *rchild;
};
一、 前序遍历
1. 递归的算法:
前序遍历每次先访问根结点,然后访问左子树,接着是右子树。用递归的方法很容易写出递归的算法:
void preTraverse(BTree *t)
{
cout<<t->data<<" ";
if(t->lchild)
preTraverse(t->lchild);
if(t->rchild)
preTraverse(t->rchild);
}
先访问根节点,然后递归访问左子树,接着是右子树。
2. 非递归算法:
由于函数的递归调用归根到底使用的是栈保存相关的变量,我们可以按照递归调用的过程,借助于栈将递归的算法改成非递归的算法。
void preOrder(BTree *t)
{
stack<BTree *> stk;
stk.push(t);
while(!stk.empty())
{
BTree *q = stk.top();
stk.pop();
cout<<q->data<<" ";
if(q->rchild)
stk.push(q->rchild);
if(q->lchild)
stk.push(q->lchild);
}
cout<<endl;
}
这里我们采用一个栈保存访问过的节点,先访问根节点,然后将右子树根节点入栈,接着是左子树根节点。之所以先右后左,因为栈是后进先出的数据结构,我们要先访问左子树后访问右子树,所以要先将右子树根节点入栈再将左子树根节点入栈。
二、 中序遍历
1. 递归算法:
中序遍历先访问左子树,然后访问根节点,接着访问右子树。递归的算法如下:
void inTraverse(BTree *t)
{
if(t->lchild)
inTraverse(t->lchild);
cout<<t->data<<" ";
if(t->rchild)
inTraverse(t->rchild);
}
2. 非递归算法:
中序遍历的时候,先访问左子树,再访问根节点,接着是右子树,那么我们需要用栈保存未访问的节点。当节点的左子树已经被访问之后,就将节点出栈,进行访问,然后将右子树根节点入栈。
void inOrder(BTree *t)
{
stack<BTree *> stk;
BTree *p = t;
while(!stk.empty() || p)
{
while(p)
{
stk.push(p);
p = p->lchild;
}
p = stk.top();
cout<<p->data<<" ";
stk.pop();
p = p->rchild;
}
cout<<endl;
}
对于每个节点,都向左走到尽头,直至没有左孩子的时候,就访问这个节点,接着访问其右孩子。如果没有右孩子或者右孩子已经访问,就继续回溯。
三、 后序遍历
1. 递归算法:
后序遍历的时候先访问左子树,然后是右子树,最后才访问根节点。递归算法如下:
void postTraverse(BTree *t)
{
if(t->lchild)
postTraverse(t->lchild);
if(t->rchild)
postTraverse(t->rchild);
cout<<t->data<<" ";
}
2. 非递归算法:
后序遍历的非递归算法较复杂,因为后续遍历需要确认左右子树都访问之后才访问根节点,因此,不仅需要栈保存节点,而且需要有状态指示其左子树和右子树都已经被访问过了,这个时候才可以访问根节点。
void postOrder(BTree *t)
{
stack<BTree *> stk;
vector<int> tag;
int k = -1;
BTree *p = t;
while(!stk.empty() || p)
{
while(p)
{
stk.push(p);
tag.push_back(1);
++k;
p = p->lchild;
}
while(!stk.empty() && tag.back() == 2){
p = stk.top();
cout<<p->data<<" ";
stk.pop();
tag.pop_back();
--k;
}
if(stk.empty())
break;
p = stk.top();
tag[k] = 2;
p = p->rchild;
}
cout<<endl;
}
以上用一个tag来存储访问的状态,1表示访问了左孩子,2表示访问了右孩子。当节点的tag为2 的时候,表示其左右孩子都已经被访问过了,这个时候就可以访问这个节点。
四、 层序遍历
二叉树的层序遍历有点类似于图的广度优先搜索算法,先访问根节点,接着将临近的每个节点都入队列,按照入队列/出队列的顺序访问每个元素。
//层序遍历---非递归---借助于队列
void levelOrder(BTree *t)
{
queue<BTree *> Q;
Q.push(t);
while(!Q.empty())
{
BTree *q = Q.front();
Q.pop();
cout<<q->data<<" ";
if(q->lchild)
Q.push(q->lchild);
if(q->rchild)
Q.push(q->rchild);
}
cout<<endl;
}
五、 总的代码:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
struct BTree{
int data;
BTree *lchild;
BTree *rchild;
BTree(){
this->data = 0;
this->lchild = NULL;
this->rchild = NULL;
}
BTree(int d){
this->data = d;
this->lchild = NULL;
this->rchild = NULL;
}
};
BTree* buildNode(int a[], int k);
void PrintTree(BTree *p);
void preTraverse(BTree *t);
void inTraverse(BTree *t);
void postTraverse(BTree *t);
void levelOrder(BTree *t);
void preOrder(BTree *t);
void inOrder(BTree *t);
void postOrder(BTree *t);
int main(int argc, char* argv[])
{
int n;
while(cin>>n)
{
int *a = new int[n+1];
a[0] = n;
for(int k = 1; k <= n; ++k)
cin>>a[k];
BTree *t = buildNode(a, 1);
PrintTree(t);
cout<<"**************levelOrder--non-recursive********"<<endl;
levelOrder(t);
cout<<"*************************************************"<<endl;
cout<<"--------------preTraverse--recursive-------------"<<endl;
preTraverse(t);
cout<<endl<<"-------------------------------------------------"<<endl;
cout<<"**************preOrder--non-recursive********"<<endl;
preOrder(t);
cout<<"*************************************************"<<endl;
cout<<"--------------inTraverse--recursive-------------"<<endl;
inTraverse(t);
cout<<endl<<"-------------------------------------------------"<<endl;
cout<<"**************inOrder--non-recursive********"<<endl;
inOrder(t);
cout<<"*************************************************"<<endl;
cout<<"--------------postTraverse--recursive-------------"<<endl;
postTraverse(t);
cout<<endl<<"-------------------------------------------------"<<endl;
cout<<"**************postOrder--non-recursive********"<<endl;
postOrder(t);
cout<<"*************************************************"<<endl;
}
return 0;
}
//后续遍历---非递归
void postOrder(BTree *t)
{
stack<BTree *> stk;
vector<int> tag;
int k = -1;
BTree *p = t;
while(!stk.empty() || p)
{
while(p)
{
stk.push(p);
tag.push_back(1);
++k;
p = p->lchild;
}
while(!stk.empty() && tag.back() == 2){
p = stk.top();
cout<<p->data<<" ";
stk.pop();
tag.pop_back();
--k;
}
if(stk.empty())
break;
p = stk.top();
tag[k] = 2;
p = p->rchild;
}
cout<<endl;
}
//中序遍历---非递归
void inOrder(BTree *t)
{
stack<BTree *> stk;
BTree *p = t;
while(!stk.empty() || p)
{
while(p)
{
stk.push(p);
p = p->lchild;
}
p = stk.top();
cout<<p->data<<" ";
stk.pop();
p = p->rchild;
}
cout<<endl;
}
//先序遍历---非递归----借助于栈
void preOrder(BTree *t)
{
stack<BTree *> stk;
stk.push(t);
while(!stk.empty())
{
BTree *q = stk.top();
stk.pop();
cout<<q->data<<" ";
if(q->rchild)
stk.push(q->rchild);
if(q->lchild)
stk.push(q->lchild);
}
cout<<endl;
}
//层序遍历---非递归---借助于队列
void levelOrder(BTree *t)
{
queue<BTree *> Q;
Q.push(t);
while(!Q.empty())
{
BTree *q = Q.front();
Q.pop();
cout<<q->data<<" ";
if(q->lchild)
Q.push(q->lchild);
if(q->rchild)
Q.push(q->rchild);
}
cout<<endl;
}
//后序遍历
void postTraverse(BTree *t)
{
if(t->lchild)
postTraverse(t->lchild);
if(t->rchild)
postTraverse(t->rchild);
cout<<t->data<<" ";
}
//中序遍历
void inTraverse(BTree *t)
{
if(t->lchild)
inTraverse(t->lchild);
cout<<t->data<<" ";
if(t->rchild)
inTraverse(t->rchild);
}
//pre-order
void preTraverse(BTree *t)
{
cout<<t->data<<" ";
if(t->lchild)
preTraverse(t->lchild);
if(t->rchild)
preTraverse(t->rchild);
}
BTree* buildNode(int a[], int k){//构建第k个Node
if(k > a[0] || a[k] == -1)
return NULL;
BTree *p = new BTree(a[k]);
p->lchild = buildNode(a, 2*k);
p->rchild = buildNode(a, 2*k+1);
return p;
}
void PrintTree(BTree *p)
{
int k = 1, cnt = 0;
queue<BTree *> que;
que.push(p);
while(!que.empty())
{
BTree *q = que.front();
que.pop();
if(q->lchild != NULL)
que.push(q->lchild);
if(q->rchild != NULL)
que.push(q->rchild);
cout<<q->data<<" ";
++cnt;
if(cnt == k){
cout<<endl;
k = 2*k;
cnt = 0;
}
}
if(cnt != 0)
cout<<endl;
}