二叉树的非递归遍历实现(附详细注释)
前序遍历
void PreorderTraversal(BinTree BT) //前序非递归遍历;
{
BinTree T = BT;
stack<BinTree> s;
while (T || s.size())
{
while (T) //先递归访问左子树;
{
cout << T->Data << ' ';
s.push(T);
T = T->Left;
}
if (s.empty())
return;
else
{
T = s.top();
s.pop(); //得到当前结点的父亲结点;
T = T->Right; //访问右结点;
}
}
}
中序遍历
void InorderTraversal(BinTree BT) { //中序遍历非递归输出(与前序遍历相比只是变了输出的位置)
BinTree T = BT;
stack<BinTree> s;
while (T || s.size()) {
while (T) {
s.push(T);
T = T->Left;
}
if (s.size()) {
T = s.top();
s.pop();
cout << T->Data << ' ';
T = T->Right;
}
}
}
后序遍历
void PostorderTraversal(BinTree BT) { //后序遍历非递归;
BinTree T = BT;
stack<stackElemType> s;
while (T || s.size()) {
while (T) { //T存在 (if,while无区别)
stackElemType t;
t.link = T;
t.tag = 0;
s.push(t);
T = T->Left;
}
//else //T为空,即叶子或已经两次访问;
//{
T = s.top().link;
int sign = s.top().tag;
s.pop();
if (sign == 0) //第一次访问该结点;
{
stackElemType t;
t.link = T;
t.tag = 1;
s.push(t);
T = T->Right;
}
else //二次访问该结点;
{
printf("%c ", T->Data);
T = NULL; //根据后序遍历的特点,控制不再访问左子树;
//即后续遍历打印完该结点后代表该结点及其以下子树以全部遍历完毕,设为空;
//注:此时的栈顶元素为输出的T的父亲结点;
}
//}
}
}
层序遍历
void LevelorderTraversal(BinTree BT) //层序遍历非递归;
{
BinTree T = BT;
queue<BinTree> q;
if (BT)
q.push(T);
else
return;
while (q.size())
{
BinTree s = q.front(); //队头;
q.pop();
cout << s->Data << ' ';
if(s->Left)
q.push(s->Left);
if(s->Right)
q.push(s->Right);
}
}
完整代码(含测试数据)
测试数据:
测试数据: A B D G # # H # # # C E # I # # F # # (前序遍历建树)
完整代码
#include<cstdio>
#include<iostream>
#include<stack>
#include<queue>
using namespace std;
typedef struct TNode* Position;
typedef Position BinTree;
typedef char ElementType;
struct TNode {
char Data; //结点数据 ElementType可为任何数据类型
BinTree Left; //指向左子树
BinTree Right; //指向右子树
};
typedef struct stackElement { //定义栈的基本元素
BinTree link;
int tag;
}stackElemType;
//先序遍历生成二叉树;
/* 由于涉及给 T 分配存储空间和给 T 赋值,故传入指向 T 的指针 */
void CreateBinTree(BinTree* T) {
ElementType num; //假设 ElementType 为 char 型
cin >> num;
if (num == '#') //num等于 # 表示空树
*T = NULL;
else {
*T = (BinTree)malloc(sizeof(struct TNode));
if (!(*T))
exit(1); //分配内存失败,退出函数
(*T)->Data = num;
CreateBinTree(&(*T)->Left); //递归构造左子树
CreateBinTree(&(*T)->Right); //递归构造右子树
}
}
void PreorderTraversal(BinTree BT) //前序非递归遍历;
{
BinTree T = BT;
stack<BinTree> s;
while (T || s.size())
{
while (T) //先递归访问左子树;
{
cout << T->Data << ' ';
s.push(T);
T = T->Left;
}
if (s.empty())
return;
else
{
T = s.top();
s.pop(); //得到当前结点的父亲结点;
T = T->Right; //访问右结点;
}
}
}
void InorderTraversal(BinTree BT) { //中序遍历非递归输出(与前序遍历相比只是变了输出的位置)
BinTree T = BT;
stack<BinTree> s;
while (T || s.size()) {
while (T) {
s.push(T);
T = T->Left;
}
if (s.size()) {
T = s.top();
s.pop();
cout << T->Data << ' ';
T = T->Right;
}
}
}
void PostorderTraversal(BinTree BT) { //后序遍历非递归;
BinTree T = BT;
stack<stackElemType> s;
while (T || s.size()) {
while (T) { //T存在 (if,while无区别)
stackElemType t;
t.link = T;
t.tag = 0;
s.push(t);
T = T->Left;
}
//else //T为空,即叶子或已经两次访问;
//{
T = s.top().link;
int sign = s.top().tag;
s.pop();
if (sign == 0) //第一次访问该结点;
{
stackElemType t;
t.link = T;
t.tag = 1;
s.push(t);
T = T->Right;
}
else //二次访问该结点;
{
printf("%c ", T->Data);
T = NULL; //根据后序遍历的特点,控制不再访问左子树;
//即后续遍历打印完该结点后代表该结点及其以下子树以全部遍历完毕,设为空;
//注:此时的栈顶元素为输出的T的父亲结点;
}
//}
}
}
void LevelorderTraversal(BinTree BT) //层序遍历非递归;
{
BinTree T = BT;
queue<BinTree> q;
if (BT)
q.push(T);
else
return;
while (q.size())
{
BinTree s = q.front(); //队头;
q.pop();
cout << s->Data << ' ';
if(s->Left)
q.push(s->Left);
if(s->Right)
q.push(s->Right);
}
}
int main()
{
BinTree T;
CreateBinTree(&T);
cout << "前序遍历为:" << endl;
PreorderTraversal(T);
cout << endl;
cout << "中序遍历为:" << endl;
InorderTraversal(T);
cout << endl;
cout << "后序遍历为:" << endl;
PostorderTraversal(T);
cout << endl;
cout << "层序遍历为:" << endl;
LevelorderTraversal(T);
return 0;
}
//测试数据: A B D G # # H # # # C E # I # # F # # (前序遍历建树)
实现与理解难点:
- 用栈模拟递归的逻辑,先递归左子树(对左子树循环访问压栈直到空),后递归右子树(对右子树一个结点访问压栈后再次重复)。
- 栈中存的元素顺序与递归顺序相同,栈顶的元素永远为当前访问结点的父亲结点(即递归返回结点),弹栈相当于递归中返回上一层。
- 后续非递归遍历较为复杂,特别注意一个结点输出的条件为第二次访问到(左右子树都已经遍历完才输出)。同时输出完该结点后注意将结点赋为NULL,表示该结点及其之下的子树已全部输出完毕,同时控制T不再访问左子树。
- 层序遍历用队列实现。
- 建议利用最后给的样例手动模拟实现,可以理解更深。