当前位置:网站首页>后序线索二叉树
后序线索二叉树
2022-06-25 22:02:00 【乖乖怪123】
后序线索二叉树的构造
三叉链表结构
结构体要用三叉链表,因为在遍历中序线索二叉树的时候需要找到某个节点的后继结点,对于右孩子来讲,其后继结点即为它的双亲,所以需要找到其双亲结点,故要用三叉链表
bool CreateThreadTree(ThreadTree &T, ThreadTree parent)树的创建需要多加一个parent参数,对于根节点的parent置为NULL, 在创建树的时候就传入NULL作为参数
后序线索二叉树和中序,前序有所不同,需要特别注意
typedef struct ThreadNode{
ElemType data;//数据元素
struct ThreadNode *lchild,*rchild,*parent;//左右孩子指针,指向双亲的指针@@@
int ltag;//
int rtag;//左右线索标记
}ThreadNode,*ThreadTree;
PostThread
后序线索二叉树的构造, 左右根
注意体会@@@处的作用
//后序线索二叉树的构造, 左右根
void PostThread(ThreadTree &p,ThreadTree &pre){
if(p){
if(p->ltag != 1)//@@@ 这里因为p->lchild可能是p的前驱结点,//如果不判断ltag,则会死循环,
PostThread(p->lchild,pre);//递归,线索化左子树
if(p->rtag != 1)//@@@
PostThread(p->rchild, pre);//递归,线索化右子树
if(p->lchild == NULL){
//左子树为空,建立前驱线索
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL && pre->rchild==NULL){
pre->rchild=p;//建立前驱结点的后继线索
pre->rtag=1;
}
pre=p;//标记当前结点成为刚刚访问过的结点
}//if(p != NULL)
}
CreatePostThread
通过后序遍历建立后序线索二叉树:
注意:因为后序遍历根为最后一个被遍历的结点,所以不能简单地用pre->rchild==NULL;而是需要判断其有无右孩子
//通过后序遍历建立后序线索二叉树的主过程算法如下:
void CreatePostThread(ThreadTree &T){
ThreadTree pre=NULL;
if(T){
//非空二叉树,线索化
PostThread(T,pre);//线索化二叉树
/* pre->rchild==NULL;//处理遍历的最后一个结点 pre->rtag=1; */
if(pre->rchild==NULL){
//@@@
//因为后序遍历的处理遍历的最后一个结点是整颗树的根节点,
//所以不能简单地pre->rchild==NULL
pre->rtag=1;
}
else{
pre->rtag=0;
}
//printf("CreatePostThread Finished\n");
}
}
后序线索二叉树的遍历
Firstnode
求后序线索二叉树中,后序序列下的第一个结点
思路:
- 先找到最左下结点,但此结点不一定是叶子结点,因 为它可能有右孩子,但是没有左孩子
- 再看它有无右孩子,有则 重复 1. ,即递归Firstnode
- 无右孩子,说明它就是要找的结点,return p
//求后序线索二叉树中,后序序列下的第一个结点
ThreadNode *Firstnode(ThreadNode *p){
while(p->ltag==0){
//先走到最左下结点,不一定是叶子结点,
p=p->lchild;//因为它可能有右孩子,而没有左孩子
}
if(p->rtag == 0){
//再看它有无右孩子,若有右孩子,则递归Firstnode
Firstnode(p->rchild);
}
else{
//若无右孩子,说明它为要找的结点,return
return p;
}
}
Nextnode
求后序线索二叉树中,结点p在后序序列下的后继
1.先判断有无双亲,无双亲说明为根节点,即后序遍历的最后一个结点
2.有双亲则看它父节点有无右孩子,且自己不是右孩子!!!,(如果自己就是那个右孩子的话,递归的话会死循环,因为他们之间的指针形成了个圈),
若父节点有右孩子,则Fistenode(父节点的右孩子), 即找到父节点右孩子(父节点的右孩子可能是一棵小型子树)的后续遍历的第一个结点
3.若父节点无右孩子,说明应该遍历父节点了。任何结点都有父节点(除了根节点)
//求后序线索二叉树中,结点p在后序序列下的后继
ThreadNode *Nextnode(ThreadNode *p){
if(!p->parent){
//说明p无双亲,p为根节点
printf("根节点在后序遍历中为最后一个结点\n");
return NULL;
}
if(p->parent->rtag==0 && **p->parent->rchild != p**){
//右孩子指针
return Firstnode(p->parent->rchild);
}
else {
// ltag==1 直接返回后继线索
return p->parent;
}
}
完整测试代码c++
//后序线索二叉树
#include<stdio.h>
#include<stdlib.h>
#define ElemType char
//tag为0表示指向左/右孩子,为1表示指向结点的前驱/后继
typedef struct ThreadNode{
ElemType data;//数据元素
struct ThreadNode *lchild,*rchild,*parent;//左右孩子指针,指向双亲的指针@@@
int ltag;//
int rtag;//左右线索标记
}ThreadNode,*ThreadTree;
void visit(ThreadTree T){
printf("%c ",T->data);
}
//后序线索二叉树的构造, 左右根
void PostThread(ThreadTree &p,ThreadTree &pre){
if(p){
if(p->ltag != 1)//@@@ 这里因为p->lchild可能是p的前驱结点,//如果不判断ltag,则会死循环,
PostThread(p->lchild,pre);//递归,线索化左子树
if(p->rtag != 1)//@@@
PostThread(p->rchild, pre);//递归,线索化右子树
if(p->lchild == NULL){
//左子树为空,建立前驱线索
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL && pre->rchild==NULL){
pre->rchild=p;//建立前驱结点的后继线索
pre->rtag=1;
}
pre=p;//标记当前结点成为刚刚访问过的结点
}//if(p != NULL)
}
//通过后序遍历建立后序线索二叉树的主过程算法如下:
void CreatePostThread(ThreadTree &T){
ThreadTree pre=NULL;
if(T){
//非空二叉树,线索化
PostThread(T,pre);//线索化二叉树
/* pre->rchild==NULL;//处理遍历的最后一个结点 pre->rtag=1; */
if(pre->rchild==NULL){
//因为后序遍历的处理遍历的最后一个结点是整颗树的根节点,
//所以不能简单地pre->rchild==NULL
pre->rtag=1;
}
else{
pre->rtag=0;
}
//printf("CreatePostThread Finished\n");
}
}
//求后序线索二叉树中,后序序列下的第一个结点
ThreadNode *Firstnode(ThreadNode *p){
while(p->ltag==0){
//先走到最左下结点的结点,不一定是叶子结点,
p=p->lchild;//因为它可能有右孩子,而没有左孩子
}
if(p->rtag == 0){
//再看它有无右孩子,若有右孩子,则递归Firstnode
Firstnode(p->rchild);
}
else{
//若无右孩子,说明它为要找的结点,return
return p;
}
}
//求后序线索二叉树中,结点p在后序序列下的后继
ThreadNode *Nextnode(ThreadNode *p){
if(!p->parent){
//说明p无双亲,p为根节点
//printf("根节点在后序遍历中为最后一个结点\n");
return NULL;
}
if(p->parent->rtag==0 && p->parent->rchild != p){
//右孩子指针,且右孩子不是自己,说明自己是左孩子 @@@
return Firstnode(p->parent->rchild);
}
else {
// ltag==1 直接返回后继线索
return p->parent;
}
}
利用上面的两个算法,可以写出不含头结点的后序线索二叉树的后序遍历算法
void Postorder(ThreadNode *T){
for(ThreadNode *p=Firstnode(T);p!=NULL;p=Nextnode(p)){
visit(p);
}
}
//创建线索二叉树,按前序输入, #表示空节点
bool CreateThreadTree(ThreadTree &T, ThreadTree parent){
ElemType ch;
scanf("%c", &ch);
if(ch == '#'){
//printf("您要创建一棵空树吗?\n");
T=NULL;
return false;
}
else{
T=(ThreadTree)malloc(sizeof(ThreadNode));
T->ltag=T->rtag=0;
if(!T){
printf("malloc failure\n");
return false;
}
T->data = ch;
T->parent = parent;
CreateThreadTree(T->lchild,T);
CreateThreadTree(T->rchild,T);
return true;
}
}
//后序销毁 左右根
bool DestroyThreadTree(ThreadTree T){
if(T==NULL){
printf("空节点\n");
return false;
}
if(T->ltag!=1){
//@@@说明左指针指向的是孩子结点,而非线索标志
DestroyThreadTree(T->lchild);
}
//printf("%c->rtag %d\n",T->data,T->rtag);
if(T->rtag!=1){
//@@@
DestroyThreadTree(T->rchild);
}
printf("销毁%c\n",T->data);
free(T);//@@@'
T=NULL;
return true;
}
//后序递归遍历线索二叉树
void PostOrder(ThreadTree T){
if(T){
if(T->ltag!=1)
PostOrder(T->lchild);
if(T->rtag != 1)
PostOrder(T->rchild);
visit(T);
}
}
int main(){
ThreadTree T=NULL;
printf("按前序输入二叉树中节点的值(输入#表示空节点)\n");
CreateThreadTree(T,NULL);//输入前序,建立二叉树
CreatePostThread(T);//通过后序遍历建立先序线索二叉树
ThreadNode *p=Firstnode(T);//求后序遍历下的第一个结点
printf("\n后序遍历的第一个结点p: %c\n",p->data);
ThreadNode* r=Nextnode(p);//求后序遍历下p的后继
printf("p的后继r: %c\n",r->data);
printf("后序遍历线索二叉树(递归PostOrder ≈ 正常BinaryTree遍历): \n");
PostOrder(T);
printf("\n");
printf("\n后序遍历线索二叉树(非递归Postorder ≈ Firstnode+Nextnode): \n");
Postorder(T);
printf("\n用完要记得销毁哦!\n");
DestroyThreadTree(T);
return 0;
}
测试样例1
在输入窗口输入: AB#D##C##

测试样例2

前序输入: ABD##E##C#G##
后序输出: DEBGCA**

边栏推荐
- cookie、session、token
- Beacon realizes asset management and indoor positioning based on 5.2 ultra-low power Bluetooth module efr32 (bg22ax)
- Analysis on resource leakage /goroutine leakage / memory leakage /cpu full in go
- Episode 3: thread synchronization using thread lock
- Share a downloaded osgeo4w64 Library Based on qgis3.10
- Leaky API interface practical development series (13): gooseneck cloud service php-api two-dimensional array parameter transfer solution
- golang Make a list of intervals with sequential numbers
- YUV444、YUV422、YUV420、YUV420P、YUV420SP、YV12、YU12、NV12、NV21
- 信息学奥赛一本通 1353:表达式括号匹配(stack) | 洛谷 P1739 表达式括号匹配
- (serial port Lora module) centrida rf-al42uh private protocol test at instruction test communication process
猜你喜欢

Summary of common JDBC exceptions and error solutions

UE4 学习记录一 创建角色,并控制其移动

Idea FAQ collection

28 rounds of interviews with 10 companies in two and a half years (including byte, pinduoduo, meituan, Didi...)

character string

Hibernate architecture introduction and environment construction (very detailed)

做接口测试,这3种工具到底什么时候用?

YUV444、YUV422、YUV420、YUV420P、YUV420SP、YV12、YU12、NV12、NV21

软件测试面试一直挂,面试官总是说逻辑思维混乱,怎么办?

【无标题】打开一个项目连接,无法正常显示时,ping一下ip
随机推荐
【无标题】打开一个项目连接,无法正常显示时,ping一下ip
Technology blog site collection
C1. k-LCM (easy version)-Codeforces Round #708 (Div. 2)
[opencv450 samples] read the image path list and maintain the proportional display
CSDN添加页内跳转和页外指定段落跳转
golang Make a list of intervals with sequential numbers
B. Box Fitting-CodeCraft-21 and Codeforces Round #711 (Div. 2)
C2. k-LCM (hard version)-Codeforces Round #708 (Div. 2)
UE4 learning record 2 adding skeleton, skin and motion animation to characters
QLabel 文字水平滚动显示
关于go协程超时退出控制条件与方式分析
Qtcreator formatting code
28 rounds of interviews with 10 companies in two and a half years (including byte, pinduoduo, meituan, Didi...)
hiberate实体类CURD、事务操作汇总
Qt Utf8 与 Unicode 编码的互相转换, Unicode编码输出为格式为 &#xXXXX
leetcode_ 136_ A number that appears only once
Leetcode(435)——无重叠区间
Leetcode (605) -- flower planting
Idea shortcut
录屏转gif的好用小工具ScreenToGif,免费又好用!