5.5遍历二叉树和线索二叉树
5.5遍历二叉树和线索二叉树
遍历二叉树的非递归算法
中序遍历的非递归算法
二叉树中序遍历的非递归算法的关键:在中序遍历过某节点的整个左子树后,如何找到该节点的根以及右子树。
基本思想:
- 建立一个栈
- 根结点进栈,遍历左子树
- 根结点出栈,输出根结点,遍历右子树。
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); printf("%c",q->data);
p = q->rchild;}
}
return OK;
}
二叉树的层次遍历
层次遍历结果:abfcdgeh
对于一颗二叉树,从根结点开始,按从上到下、从左到右的顺序访问每一个结点。
每个结点仅仅访问一次。
算法设计思路: 使用一个队列
- 将根结点进队;
- 队不空时循环:从队列中出列一个结点*p,访问它;
- 若它有左孩子结点,将左孩子结点进队;
- 若它有右孩子结点,将右孩子结点进队。
使用队列类型定义如下:
typedef struct{
BTNode data[MaxSize]; //存放队中元素
int font, rear; //对头和队尾指针
}SqQueue; //顺序循环队列类型
二叉树层次遍历算法:
void LevelOrder(BTNode *b){
BTNode *p; SqQueue *qu;
InitQueue(qu); //初始化队列
enQueue(qu, b); //根结点指针进入队列
while(!QueueEmpty(qu)) { //队不为空,则循环
deQueue(qu, p); //出队结点p
printf("%c", p->data); //访问结点p
if(p->lchild != NULL) enQueue(qu, p->lchild); //有左孩子时将其进队
if(p->rchild != NULL) enQueue(qu, p->rchild); //有右孩子时将其进队
}
}
二叉树遍历算法的应用——二叉树的建立
按先序遍历序列建立二叉树的二叉链表
例:已知先序序列为:ABCDEGF
- 从键盘输入二叉树的结点信息,建立二叉树的存储结构;
- 在建立二叉树的过程中按照二叉树先序方式建立;
为了确保生成树唯一,补充空结点(#)!!!
对如图所示二叉树,按下列顺序读入字符:ABC##DE#G##F###
Status CreateBiTree(BiTree &T){
scanf(&ch); //cin>>ch;
if(ch == "#") T = NULL;
else{
if(!(T = (BiTree)*malloc(sizeof(BiTNode))))
exit(OVERFLOW);//T = new BiTNode;
T->data = ch;
CreateBiTree(T->lchild); //构造左子树
CreateBiTree(T->rchild); //构造右子树
}
return OK;
}//CreateBiTree
二叉树遍历算法的应用——复制二叉树
- 如果是空树,递归结束;
- 否则,申请新结点空间,复制根结点
- 递归复制左子树
- 递归复制右子树
int Copy(BiTree T,BiTree &NewT){
if(T == NULL) //如果是空树,返回0
{
NewT = NULL; return 0;
}
else{
NewT = new BiNode; NewT->data = T->data;
Copy(T->lChild, NewT->lchild);
Copy(T->rChild, NewT->rchild);
}
}
二叉树遍历算法的应用——计算二叉树深度
-
如果是空树,则深度为0;
-
否则,递归计算左子树的深度记为m,递归计算右子树的深度记为n,二叉树的深度则为m与n的较大者加1.
int Depth(BiTree T){
if(T == NULL) return 0; //如果是空树返回0
else{
m = Depth(T->lchild);
n = Depth(T->rchild);
if(m > n) return (m+1);
else return (n+1);
}
}
二叉树遍历算法的应用——计算二叉树结点总数
-
如果是空树,则结点个数为0;
-
否则,结点个数为左子树的结点个数+右子树的结点个数再+1.
int Node(BiTree T){
if(T == NULL)
rerurn 0;
else
return NodeCount(T->lchild) + NodeCount(T->rchild) + 1;
}
二叉树遍历算法的应用——计算二叉树叶子结点数
- 如果是空树,则叶子结点个数为0;
- 否则,为左子树的叶子结点个数+右子树的叶子结点个数。
int LeadCount(BiTree T){
if(T == NULL) //如果是空树返回0
return 0;
if(T->lchild == NULL && T->rchild == NULL)
return 1; //如果是叶子结点返回1
else
return LeafCount(T->lchild) + LeafCount(T->rchild);
}
线索二叉树
问题:为什么要研究线索二叉树?
当二叉链表作为二叉树的存储结构时,可以很方便地找到某个结点的左右孩子;但一般情况下,无法直接找到该结点在某种遍历序列中的前驱和后继结点。
提出问题:如何寻找特定遍历序列中二叉树结点的前驱和后继???
解决方法:
- 通过遍历寻找——费时间
- 再增设前驱、后继指针域——增加了存储负担
- 利用二叉链表中的空指针域
如果某个结点的左孩子为空,则将空的左孩子指针域改为指向其前驱;如果某结点的右孩子为空,则将空的右孩子指针域改为指向其后继——这种改变指向的指针称为 “线索” 。
加上了线索的二叉树为 线索二叉树(Threaded Binary Tree)
对二叉树按某种遍历次序时期变为线索二叉树的过程叫 线索化。
为区分lchild和rchild指针到底是指向孩子的指针,还是指向前驱或者后继的指针,对二叉链表中每个结点增设两个标志域ltag和rtag,并约定:
ltag = 0 | lchild指向该结点的左孩子 |
ltag = 1 | lchild指向该结点的前驱 |
rtag = 0 | lchild指向该结点的右孩子 |
rtag = 1 | lchild指向该结点的后驱 |
这样,结点的结构为:
lchild | itag | data | rtag | rchild |
---|
typedef struct BiThrNode{
int data;
int ltag, rtag;
struct BiThrNode *lchild, rchild;
}BiThrNode, *BiThrTree;
先序线索二叉树 中序线索二叉树 后序线索二叉树
为避免悬空态,增设一个头结点
增设了一个头结点:ltag=0,lchild指向根节点,rtag=1,rchild指向遍历序列中最后一个结点,遍历序列中第一个结点的lc域和最后一个结点的rc域都指向头结点
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!