二叉树的遍历总结
二叉树结构
用二叉链表来存储二叉树
typedef struct BiTNode //二叉树
{
int data; //结点数值
struct BiTNode *lchild;
struct BiTNode *rchild;
}BiTNode, *BiTree;
建立二叉树
输入一个数组,以层次遍历的顺序进行建树,规定数组元素都大于0。当数组元素为-1时,表示该结点为空。
举个例子,比如数组大小为 12,数组元素为
3 5 6 8 -1 2 7 8 1 34 -1 65
则建立的二叉树为
建立二叉树的代码:
queue<BiTree> q;//辅助队列
void createBiTree()
{
int n;
int a[1010];
//读取输入
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
//建立二叉树
int counts = 1;
for (int i = 1; i <= n; i++)
{
BiTree tmp = (BiTree)malloc(sizeof(BiTNode));
tmp = q.front();
q.pop();
tmp->data = a[i];//赋值
tmp->lchild = (BiTree)malloc(sizeof(BiTNode));
tmp->rchild = (BiTree)malloc(sizeof(BiTNode));
if(a[i] != -1)
{
tmp->num = counts;//给非空结点编号
counts++;
q.push(tmp->lchild);//后面的元素必须挂在非空结点下面
q.push(tmp->rchild);
}
else //空结点标记
{
tmp->lchild->data = -1;
tmp->rchild->data = -1;
}
}
while(!q.empty())//清除队列中的空结点
{
q.front()->data = -1;
q.pop();
}
}
层次遍历
访问结点
- 如果该结点不为空,输出该结点的值
void visit(BiTree T)
{
if(T->data != -1)
printf("%d ", T->data);
}
层次遍历
- 先把二叉树的根结点入队。然后队首元素出队,访问出队结点。若它有左子树,则将左子树根结点入队;若它有右子树,右子树根结点入队。然后继续出队,访问出队结点······重复这个过程,直到队列为空
void LevelOrder(BiTree T)
{
while(!q.empty())//初始化队列
{
q.pop();
}
q.push(T);//根结点入队
while(!q.empty())
{
BiTree tmp = q.front();
q.pop();//队头结点出队
visit(tmp);//访问出队结点
if(tmp->lchild->data != -1)
q.push(tmp->lchild);
if(tmp->rchild->data != -1)
q.push(tmp->rchild);
}
}
递归的三种遍历
先序遍历,也叫前序遍历
- 访问根结点
- 先序遍历左子树
- 先序遍历右子树
void PreOrder(BiTree T)
{
if(T->data != -1)//结点非空
{
visit(T);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
中序遍历
- 中序遍历左子树
- 访问根结点
- 中序遍历右子树
void InOrder(BiTree T)
{
if(T->data != -1)//结点非空
{
InOrder(T->lchild);
visit(T);
InOrder(T->rchild);
}
}
后序遍历
- 后序遍历左子树
- 后序遍历右子树
- 访问根结点
void PostOrder(BiTree T)
{
if(T->data != -1)//结点非空
{
PostOrder(T->lchild);
PostOrder(T->rchild);
visit(T);
}
}
非递归的三种遍历
中序遍历
- 沿着根的左孩子,依次入栈,直到左孩子为空
- 栈顶元素出栈并访问,若其右孩子为空,继续执行 2
- 若其右孩子不空,将右子树转向执行 1
void InOrder2(BiTree T)
{
stack<BiTree> s;
BiTree p = T;
while((p->data != -1) || (!s.empty()))//p不空或栈不空
{
if(p->data != -1) //非空结点
{
s.push(p); //入栈,一路向左
p = p->lchild;
}
else //出栈,转向出栈结点的右子树
{
p = s.top();
s.pop();
visit(p);
p = p->rchild;
}
}
}
先序遍历和中序遍历很相似,区别在于扫描结点时先访问结点,再将其入栈
void PreOrder2(BiTree T)
{
stack<BiTree> s;
BiTree p = T;
while((p->data != -1) || (!s.empty()))//p不空或者栈不空
{
if(p->data != -1) //结点非空
{
visit(p); //先访问结点,再入栈
s.push(p);
p = p->lchild;
}
else
{
p = s.top(); //出栈,转向出栈结点的右子树
s.pop();
p = p->rchild;
}
}
}
后序遍历
-
初始时依次扫描根结点的所有左侧结点并将它们全部依次进栈
-
读栈顶元素,若其右孩子不为空且未被访问过,将右子树转向 1
-
否则,栈顶元素出栈并访问
注意:
在第二步中,需要分清返回时是从左子树返回的还是右子树返回的,设置一个辅助指针 r,指向最近访问过的结点。
每次出栈访问完一个结点就相当于遍历完以该结点为根的子树,需要将 p 置空。
void PostOrder2(BiTree T)
{
stack<BiTree> s;
BiTree p = T;
BiTree r = NULL; //r指针记录最后一个访问的结点
BiTree kong = (BiTree)malloc(sizeof(BiTNode));//空指针,方便p置空
kong->data = -1;
while((p->data != -1) || (!s.empty()))//p不空或栈不空
{
if(p->data != -1) //非空结点
{
s.push(p); //入栈,一路向左
p = p->lchild;
}
else
{
p = s.top(); //访问栈顶元素(不出栈)
if((p->rchild->data != -1) && (p->rchild != r))//右子树存在且未被访问过
{
p = p->rchild; //转向右子树
s.push(p);
p = p->lchild; //仍然一路向左
}
else //否则,弹出栈顶元素并访问
{
s.pop();
visit(p);
r = p; //记录最近访问过的结点
p = kong; //结点访问完后,p置空
}
}
}
}
完整代码
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <stack>
using namespace std;
typedef struct BiTNode //二叉树
{
int data; //结点数值
struct BiTNode *lchild;
struct BiTNode *rchild;
}BiTNode, *BiTree;
queue<BiTree> q;//辅助队列
void createBiTree(); //层次序列建立二叉树
void visit(BiTree R); //访问结点
void LevelOrder(BiTree R); //层次遍历
/* 递归的先序、中序、后序遍历 */
void PreOrder(BiTree R);
void InOrder(BiTree R);
void PostOrder(BiTree R);
/* 非递归的先序、中序、后序遍历 */
void PreOrder2(BiTree R);
void InOrder2(BiTree R);
void PostOrder2(BiTree R);
int main()
{
BiTree bit;
bit = (BiTree)malloc(sizeof(BiTNode));
q.push(bit);
createBiTree();
//层次遍历
printf("\n层次遍历: ");
LevelOrder(bit);
printf("\n\n");
/* 递归 */
printf("递归\n");
printf("先序遍历: ");
PreOrder(bit);
printf("\n");
printf("中序遍历: ");
InOrder(bit);
printf("\n");
printf("后序遍历: ");
PostOrder(bit);
printf("\n\n");
/* 非递归 */
printf("非递归\n");
printf("先序遍历: ");
PreOrder2(bit);
printf("\n");
printf("中序遍历: ");
InOrder2(bit);
printf("\n");
printf("后序遍历: ");
PostOrder2(bit);
printf("\n");
return 0;
}
void createBiTree()
{
int n;
int a[1010];
//读取输入
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
//建立二叉树
for (int i = 1; i <= n; i++)
{
BiTree tmp = (BiTree)malloc(sizeof(BiTNode));
tmp = q.front();
q.pop();
tmp->data = a[i];//赋值
tmp->lchild = (BiTree)malloc(sizeof(BiTNode));
tmp->rchild = (BiTree)malloc(sizeof(BiTNode));
if(a[i] != -1)
{
q.push(tmp->lchild);//后面的元素必须挂在非空结点下面
q.push(tmp->rchild);
}
else //空结点标记
{
tmp->lchild->data = -1;
tmp->rchild->data = -1;
}
}
while(!q.empty())//清除队列中的空结点
{
q.front()->data = -1;
q.pop();
}
}
void visit(BiTree T)
{
if(T->data != -1)
printf("%d ", T->data);
}
void LevelOrder(BiTree T)
{
while(!q.empty())//初始化队列
{
q.pop();
}
q.push(T);//根结点入队
while(!q.empty())
{
BiTree tmp = q.front();
q.pop();//队头结点出队
visit(tmp);//访问出队结点
if(tmp->lchild->data != -1)
q.push(tmp->lchild);
if(tmp->rchild->data != -1)
q.push(tmp->rchild);
}
}
void PreOrder(BiTree T)
{
if(T->data != -1)//结点非空
{
visit(T);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
void InOrder(BiTree T)
{
if(T->data != -1)//结点非空
{
InOrder(T->lchild);
visit(T);
InOrder(T->rchild);
}
}
void PostOrder(BiTree T)
{
if(T->data != -1)//结点非空
{
PostOrder(T->lchild);
PostOrder(T->rchild);
visit(T);
}
}
void PreOrder2(BiTree T)
{
stack<BiTree> s;
BiTree p = T;
while((p->data != -1) || (!s.empty()))//p不空或者栈不空
{
if(p->data != -1) //结点非空
{
visit(p); //先访问结点,再入栈
s.push(p);
p = p->lchild;
}
else
{
p = s.top(); //出栈,转向出栈结点的右子树
s.pop();
p = p->rchild;
}
}
}
void InOrder2(BiTree T)
{
stack<BiTree> s;
BiTree p = T;
while((p->data != -1) || (!s.empty()))//p不空或栈不空
{
if(p->data != -1) //非空结点
{
s.push(p); //入栈,一路向左
p = p->lchild;
}
else //出栈,转向出栈结点的右子树
{
p = s.top();
s.pop();
visit(p);
p = p->rchild;
}
}
}
void PostOrder2(BiTree T)
{
stack<BiTree> s;
BiTree p = T;
BiTree r = NULL; //r指针记录最后一个访问的结点
BiTree kong = (BiTree)malloc(sizeof(BiTNode));//空指针,方便p置空
kong->data = -1;
while((p->data != -1) || (!s.empty()))//p不空或栈不空
{
if(p->data != -1) //非空结点
{
s.push(p); //入栈,一路向左
p = p->lchild;
}
else
{
p = s.top(); //访问栈顶元素(不出栈)
if((p->rchild->data != -1) && (p->rchild != r))//右子树存在且未被访问过
{
p = p->rchild; //转向右子树
s.push(p);
p = p->lchild; //仍然一路向左
}
else //否则,弹出栈顶元素并访问
{
s.pop();
visit(p);
r = p; //记录最近访问过的结点
p = kong; //结点访问完后,p置空
}
}
}
}
程序运行结果如下: