【线索二叉树】C++代码及线索化过程详解

简介: 【线索二叉树】C++代码及线索化过程详解


我在这里不仅写了线索二叉树的普通代码,在代码中我还加入了线索化过程的打印,更好的帮助理解!

线索二叉树的概念

传统的二叉树存在很多空指针能否利用这些空指针来存放指向其前驱或后继的指针?这样就可以像遍历单链表那样方便地遍历二叉树。引入线索二叉树正是为了加快查找结点前驱和后继的速度。
若无左子树,令lchild指向其前驱结点;若无右子树,令rchild指向其后继结点。如图所示,还需增加两个标志域标识指针域是指向左(右)孩子还是指向前驱(后继)。 这就是线索二叉树



中序线索二叉树的构造

二叉树的线索化是将二叉链表中的空指针改为指向前驱或后继的线索。而前驱或后继的信息只有在遍历时才能得到,因此线索化的实质就是遍历一次二叉树
以中序线索二叉树的建立为例。附设指针 pre 指向刚刚访问过的结点,指针p 指向正在访问的结点,即pre指向p 的前驱。在中序遍历的过程中,检查p的左指针是否为空,若为空就将它指向pre;检查pre的右指针是否为空,若为空就将它指向p,如图所示。



中序线索二叉树的遍历

中序线索二叉树的结点中隐含了线索二叉树的前驱和后继信息。在对其进行遍历时,只要先找到序列中的第一个结点,然后依次找结点的后继,直至其后继为空在中序线索二叉树中找结点后继的规律是:若其右标志为“1”,则右链为线索,指示其后继,否则遍历右子树中第一个访问的结点(右子树中最左下的结点)为其后继


过程详解版代码

程序运行部分结果

代码

#include<iostream>
#include<cstdio>
#include<stdlib.h>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define TElemType char
#define Status int
/*二叉树的二叉线索表示*/
typedef enum {Link, Thread} PointerTag; //子树不为空时,Link==0 表示指针;子树为空时,Thread==1表示线索
typedef struct BiThrNode{
  TElemType data;
  struct BiThrNode *lchild, *rchild;  //左右孩子 
  PointerTag LTag, RTag;        //左右标志 
}BiThrNode, *BiThrTree; 
int cnt = 0;
/*按照先序序列建立一棵线索二叉树*/
Status CreatBiThrTree(BiThrTree &T){
  //按照先序序列输入二叉树中结点的值(一个字符),#表示空树
  char ch;
  cin >> ch;
  if(ch == '#') T = NULL;
  else{
    if(!(T = (BiThrNode*)malloc(sizeof(BiThrTree)))) exit(OVERFLOW);
    T->data = ch;       //生成根节点 
    T->LTag = Link;
    T->RTag = Link;
    CreatBiThrTree(T->lchild);  //构造左子树 
    CreatBiThrTree(T->rchild);  //构造右子树 
  }
  return OK; 
}//CreatBiThrTree
/*中序线索化*/
void Visualize(BiThrTree p, BiThrTree pre){
  //线索化的可视化输出
  cout << "p指向的结点:";
  if(p != NULL) cout << p->data << '\t';
  else cout << "NULL\t";
  cout <<  "pre指向的结点:";
  if(pre != NULL) cout << pre->data << endl;
  else cout << "NULL" << endl; 
}
Status InThread(BiThrTree &p, BiThrTree &pre){
  Visualize(p, pre);
  if(p != NULL){
    cout << "------------------------------------>线索化" << p->data << "的左子树" << endl; 
    InThread(p->lchild, pre); //递归,线索化左子树
    cout << "------------------------------------>" << p->data << "的左子树线索化完成" << '\t';
    if(p->lchild == NULL){    //如果左子树为空,建立前驱线索
      cout << p->data << "的前驱节点指向pre指向的结点" << endl; 
      p->lchild = pre;
      p->LTag = Thread;   
    }
    if(pre != NULL && pre->rchild == NULL){
      cout << pre->data << "的后继结点指向" << p->data << endl;
      pre->rchild = p;    //如果此时的前驱结点的右子树为空,建立后继线索 
      pre->RTag = Thread;
    }
    cout << "pre指向p所指向的结点" << endl;
    pre = p;          //pre后移
    cout << "------------------------------------>线索化" << p->data << "的右子树" << endl; 
    InThread(p->rchild, pre); //递归,线索化右子树 
    cout << "------------------------------------>" << p->data << "的右子树线索化完成" << endl;
    cout << ++cnt << "次线索化之后:"; 
    Visualize(p, pre);
    cout << endl << endl; 
  }//else if(p == NULL) return ERROR;//空树返回错误 
  return OK;
}//InThread
Status CreatInThread(BiThrTree &T){
  BiThrTree pre = NULL; //因为第一个结点一开始无前驱
  if(T != NULL){
    InThread(T, pre); //线索化二叉树
    pre->rchild = NULL; //处理到了最后一个结点,pre指向最后一个结点,右孩子设为空表示最后一个结点 
    pre->RTag = Thread; //表示指针 
  }
  return OK;
}//CreatInThread
/*打印结点*/
void visit(BiThrNode *p){
  cout << p->data;
}
/*中序递归遍历*/
void InOrder(BiThrTree T){
  if(T != NULL){
    InOrder(T->lchild);
    visit(T);
    InOrder(T->rchild);
  }
}
/*中序线索二叉树的遍历*/
BiThrNode *FirstNode(BiThrNode *p){
  //求中序线索二叉树中中序序列下的第一个结点
  while(p->LTag != Thread) p = p->lchild; //找到最左下方的结点(中序的第一个结点)
  return p; 
}
BiThrNode *NextNode(BiThrNode *p){
  //求中序线索二叉树中结点p在中序序列的后继结点
  if(p->RTag == Link) 
    return FirstNode(p->rchild);    //如果有右子树,继续递归向下找 
  else return p->rchild;          //RTag==1,直接返回线索 
}
void InThreadOrder(BiThrTree T){
  //不含头结点的中序线索二叉树的中序遍历算法 
  for(BiThrNode *p = FirstNode(T); p != NULL; p = NextNode(p))
    visit(p);
}
/*主函数*/
int main(){
  cout << "---------------这是一个线索二叉树程序!---------------" << endl;
  cout << "-------------首先按照先序序列建立一棵线索二叉树-------" << endl << endl;
  cout << "按照先序序列输入二叉树中结点的值(一个字符),#表示空树" << endl; 
  BiThrTree T;
  CreatBiThrTree(T);
  cout << "二叉树的递归中序遍历:"; 
  InOrder(T);
//  if(s == 1) 
  cout << endl << endl << "-------------下面进行中序线索化-------------" << endl;
  cout << "--------------打印线索化的过程--------------" << endl;
  if(CreatInThread(T) == 1) cout << "--------------已完成线索化--------------" << endl;
  cout << "中序线索二叉树的非递归遍历序列:";
  InThreadOrder(T);
  return 0;
}
//测试示例:abc##de#g##f###

纯享版代码

#include<iostream>
#include<cstdio>
#include<stdlib.h>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define TElemType char
#define Status int
/*二叉树的二叉线索表示*/
typedef enum {Link, Thread} PointerTag; //子树不为空时,Link==0 表示指针;子树为空时,Thread==1表示线索
typedef struct BiThrNode{
  TElemType data;
  struct BiThrNode *lchild, *rchild;  //左右孩子 
  PointerTag LTag, RTag;        //左右标志 
}BiThrNode, *BiThrTree; 
/*按照先序序列建立一棵线索二叉树*/
Status CreatBiThrTree(BiThrTree &T){
  //按照先序序列输入二叉树中结点的值(一个字符),#表示空树
  char ch;
  cin >> ch;
  if(ch == '#') T = NULL;
  else{
    if(!(T = (BiThrNode*)malloc(sizeof(BiThrTree)))) exit(OVERFLOW);
    T->data = ch;       //生成根节点 
    T->LTag = Link;       //这里一定要加上,不然后边会出错 
    T->RTag = Link;
    CreatBiThrTree(T->lchild);  //构造左子树 
    CreatBiThrTree(T->rchild);  //构造右子树 
  }
  return OK; 
}//CreatBiThrTree
/*中序线索化*/
Status InThread(BiThrTree &p, BiThrTree &pre){
  if(p != NULL){
    InThread(p->lchild, pre); //递归,线索化左子树
    if(p->lchild == NULL){    //如果左子树为空,建立前驱线索
      p->lchild = pre;
      p->LTag = Thread;   
    }
    if(pre != NULL && pre->rchild == NULL){
      pre->rchild = p;    //如果此时的前驱结点的右子树为空,建立后继线索 
      pre->RTag = Thread;
    }
    pre = p;          //pre后移
    InThread(p->rchild, pre); //递归,线索化右子树 
  }//else if(p == NULL) return ERROR;//空树返回错误 
  return OK;
}//InThread
Status CreatInThread(BiThrTree &T){
  BiThrTree pre = NULL; //因为第一个结点一开始无前驱
  if(T != NULL){
    InThread(T, pre); //线索化二叉树
    pre->rchild = NULL; //处理到了最后一个结点,pre指向最后一个结点,右孩子设为空表示最后一个结点 
    pre->RTag = Thread;     //表示指针 
  }
  return OK;
}//CreatInThread
/*打印结点*/
void visit(BiThrNode *p){
  cout << p->data;
}
/*中序递归遍历*/
void InOrder(BiThrTree T){
  if(T != NULL){
    InOrder(T->lchild);
    visit(T);
    InOrder(T->rchild);
  }
}
/*中序线索二叉树的遍历*/
BiThrNode *FirstNode(BiThrNode *p){
  //求中序线索二叉树中中序序列下的第一个结点
  while(p->LTag != Thread) p = p->lchild; //找到最左下方的结点(中序的第一个结点)
  return p; 
}
BiThrNode *NextNode(BiThrNode *p){
  //求中序线索二叉树中结点p在中序序列的后继结点
  if(p->RTag == Link) 
    return FirstNode(p->rchild);        //如果有右子树,继续递归向下找 
  else return p->rchild;          //RTag==1,直接返回线索 
}
void InThreadOrder(BiThrTree T){
  //不含头结点的中序线索二叉树的中序遍历算法 
  for(BiThrNode *p = FirstNode(T); p != NULL; p = NextNode(p))
    visit(p);
}
/*主函数*/
int main(){
  BiThrTree T;
  CreatBiThrTree(T);
//  InOrder(T); 
  CreatInThread(T);
  cout << "遍历序列:"; 
  InThreadOrder(T); 
  return 0;
}
//abc##de#g##f###
相关文章
|
4月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
78 2
|
4月前
|
算法框架/工具 C++ Python
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
根据相机旋转矩阵求解三个轴的旋转角/欧拉角/姿态角 或 旋转矩阵与欧拉角(Euler Angles)之间的相互转换,以及python和C++代码实现
297 0
|
1月前
|
算法 安全 C++
提高C/C++代码的可读性
提高C/C++代码的可读性
44 4
|
2月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
334 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
3月前
|
C++
继续更新完善:C++ 结构体代码转MASM32代码
继续更新完善:C++ 结构体代码转MASM32代码
|
3月前
|
C++ Windows
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
|
3月前
|
C++
2合1,整合C++类(Class)代码转换为MASM32代码的平台
2合1,整合C++类(Class)代码转换为MASM32代码的平台
|
3月前
|
前端开发 C++ Windows
C++生成QML代码与QML里面集成QWidget
这篇文章介绍了如何在C++中生成QML代码,以及如何在QML中集成QWidget,包括使用Qt Widgets嵌入到QML界面中的技术示例。
|
4月前
|
程序员 C++ 开发者
C++命名空间揭秘:一招解决全局冲突,让你的代码模块化战斗值飙升!
【8月更文挑战第22天】在C++中,命名空间是解决命名冲突的关键机制,它帮助开发者组织代码并提升可维护性。本文通过一个图形库开发案例,展示了如何利用命名空间避免圆形和矩形类间的命名冲突。通过定义和实现这些类,并在主函数中使用命名空间创建对象及调用方法,我们不仅解决了冲突问题,还提高了代码的模块化程度和组织结构。这为实际项目开发提供了宝贵的参考经验。
67 2
|
4月前
|
C++
拥抱C++面向对象编程,解锁软件开发新境界!从混乱到有序,你的代码也能成为高效能战士!
【8月更文挑战第22天】C++凭借其强大的面向对象编程(OOP)能力,在构建复杂软件系统时不可或缺。OOP通过封装数据和操作这些数据的方法于对象中,提升了代码的模块化、重用性和可扩展性。非OOP方式(过程化编程)下,数据与处理逻辑分离,导致维护困难。而OOP将学生信息及其操作整合到`Student`类中,增强代码的可读性和可维护性。通过示例对比,可以看出OOP使C++代码结构更清晰,特别是在大型项目中,能有效提高开发效率和软件质量。
37 1