递归法遍历二叉树
遍历二叉树是指按某条搜索路径巡访树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次
访问的含义很广,可以是对结点做各种处理,包括输出结点的信息,对结点进行运算和修改等
先序遍历
先序遍历二叉树的操作定义如下:
若二叉树为空,则空操作;否则
- 1、访问根结点
- 2、先序遍历左子树
- 3、先序遍历右子树
【算法描述】
Status PreOrderTraverse(BiTree T)
{
if(T==NULL) return OK;
else{
cout << T->data; //访问根结点
PreOrderTraverse(T->lchild); //先序遍历左子树
PreOrderTraverse(T->rchild); //先序遍历右子树
}
}
中序遍历
中序遍历二叉树的操作定义如下:
若二叉树为空,则空操作;否则
- 1、中序遍历左子树
- 2、访问根结点
- 3、中序遍历右子树
【算法描述】
Status InOrderTraverse(BiTree T)
{
if(T==NULL) return OK;
else{
InOrderTraverse(T->lchild); //中序遍历左子树
cout << T->data; //访问根结点
InOrderTraverse(T->rchild); //中序遍历右子树
}
}
后序遍历
后序遍历二叉树的操作定义如下:
若二叉树为空,则空操作;否则
- 1、后序遍历左子树
- 2、后序遍历右子树
- 3、访问根结点
Status PostOrderTraverse(BiTree T)
{
if(T==NULL) return OK;
else{
PostOrderTraverse(T->lchild); //后序遍历左子树
PostOrderTraverse(T->rchild); //后序遍历右子树
cout << T->data; //访问根结点
}
}
可以自己试试写出这些序列,更好的理解这个算法
如果去掉输出语句,从递归的角度看,三种算法使完全相同的,或说这三种算法的访问路径是相同的,只是访问结点的时机不同。
从虚线的出发点到终点的路径上,每个结点经过3次
第1次经过时访问=先序遍历
第2次经过时访问=中序遍历
第3次经过时访问=后序遍历
时间效率:O(n)
//每个结点只访问一次
空间效率:O(n)
//栈占用的最大辅助空间(只有一条单链)
非递归法遍历二叉树
非递归法遍历二叉树是利用栈进行遍历。算法会比递归法麻烦些
这里只举中序遍历例子
【算法步骤】
①初始化一个空栈 S,指针 p 指向根结点
②申请一个结点空间 q,用来存放栈顶弹出的元素
③当 p 非空或者栈 S 非空时,循环执行以下操作:
- 如果 p 非空,则将 p 进栈,p指向该结点的左孩子
- 如果 p 为空,则弹出栈顶元素并访问,将p指向该结点的右孩子
【算法描述】
Status InOrderTraverse(BiTree T)
{
BiTree p; InitStack(S); p=T;
while(p || !StackEmpty(S))
{
if(p) { Push(S,p); p = p->lchild; }
else { Pop(S,q); cout << q->data; //Pop(S,q),将栈顶元素弹出,并使q指向该元素
p = q->rchild; }
}
return OK;
}
层次遍历二叉树
对于一棵二叉树,从根结点开始,按从上到下、从左到右的(一层一层的)顺序访问每一个结点,且每一个结点仅仅访问一次
算法设计思路:使用一个队列
使用队列类型定义如下:
【算法步骤】
1、将根结点进队
2、队不空时循环:从队列中出列一个结点*p,访问它:
①若它有左孩子结点,将左孩子结点进队
②若它有右孩子结点,将右孩子结点进队
【算法描述】
void LevelOrder(BTNode *b)
{
BTNode *p; SqQueue *qu;
InitQueue(qu); //初始化队列
enQueue(qu,b); //根结点指针进入队列
while(!QueueEmpty(qu)) //队不为空,则循环
{
deQueue(qu,p); //出队结点p
cout << p->data;
if(p->lchild!=NULL) enQueue(qu,p->lchild);
//有左孩子时将其进队
if(p->rchild!=NULL) enQueue(qu,p->rchild);
} //有右孩子时将其进队
}