C语言数据结构之线索二叉树及其遍历

所属分类: 软件编程 / C 语言 阅读数: 98
收藏 0 赞 0 分享

C语言数据结构之线索二叉树及其遍历

遍历二叉树就是以一定的规则将二叉树中的节点排列成一个线性序列,从而得到二叉树节点的各种遍历序列,其实质是:对一个非线性的结构进行线性化。使得在这个访问序列中每一个节点都有一个直接前驱和直接后继。传统的链式结构只能体现一种父子关系,¥不能直接得到节点在遍历中的前驱和后继¥,而我们知道二叉链表表示的二叉树中有大量的空指针,当使用这些空的指针存放指向节点的前驱和后继的指针时,则可以更加方便的运用二叉树的某些操作。引入线索二叉树的目的是: 为了加快查找节点的前驱和后继。对二叉树的线索化就是对二叉树进行一次遍历,在遍历的过程中检测节点的左右指针是否为空,如果是空,则将他们改为指向前驱和后继节点的线索。

如果二叉树没有被线索化,也可以使用<<非递归>>的代码进行遍历的,但是那就需要借助于<<栈>>,但是在线索化之后,对线索化的二叉树进行<<非递归>>的遍历就不再需要栈的辅助。

实现代码:

#include<stdio.h> 
#include<stdlib.h> 
 
#define OK 1 
#define ERROR 0 
 
typedef char ElemType; 
typedef int Status; 
 
/* 定义枚举类型,其值只能是Link和Thread 
 * Link表示的该指针指示的是孩子 
 * Thread表示的是还指针指示的是前驱或者是后缀 
 * */ 
typedef enum { 
  Link,Thread 
}PointerTag; 
 
typedef struct ThrBiTrNode{ 
  ElemType data; 
  struct ThrBiTrNode *lchild, *rchild; 
  PointerTag rTag, lTag; 
}ThrBiTrNode, *ThrBiTree; 
 
ThrBiTree Pre; 
 
Status InitThreadBinaryTree(ThrBiTree* T){ 
  *T = NULL; 
  return OK; 
} 
//先序建立二叉树,lchild和rchild都是指示做孩子和右孩子,所以lTag和rTag为Link 
Status ThreadBinaryTree_PreOrderInput(ThrBiTree* T){ 
  ElemType c; 
  scanf("%c", &c); 
  if( ' ' == c ){ 
    *T = NULL; 
  } 
  else{ 
    *T = (ThrBiTrNode*)malloc(sizeof(ThrBiTrNode)); 
    if( !*T ){ 
      return ERROR; 
    } 
    (*T)->data = c; 
    (*T)->lTag = Link; 
    (*T)->rTag = Link; 
    ThreadBinaryTree_PreOrderInput(&(*T)->lchild); 
    ThreadBinaryTree_PreOrderInput(&(*T)->rchild); 
  } 
  return OK; 
} 
//<<中序遍历>>对二叉树进行<<线索化>>,但是没有提供Pre的初始值,只是给出了 
//中间的过程,递归的思想和思考方式。 
//1 线索化左子树 
//2 对双亲节点处理//递归中的base 
//3 线索化右子树 
Status InOrderThread(ThrBiTree T){ 
  if( T != NULL ){ 
    InOrderThread(T->lchild);    //线索化左子树 
     
    //对双亲节点进行线索化处理 
    if( T->lchild == NULL ){ //如果左孩子为空,则将lchild作为索引 
            //Pre指向刚刚访问的节点 
      T->lTag = Thread; 
      T->lchild = Pre; 
    } 
    if( Pre->rchild == NULL ){  //直到现在才知道Pre的后缀是T,所以 
            //要对Pre进行设置,如果Pre的rchild为空 
            //则将Pre的rchild设置为后缀,指向T 
      Pre->rTag = Thread; 
      Pre->rchild = T; 
    } 
 
    Pre = T;      //标记当前的节点称为刚刚访问过的节点 
            //Pre最后会指向树的中序遍历的最后的 
            //一个节点 
 
    InOrderThread(T->rchild);  //线索化右子树 
  } 
  return OK; 
} 
//创建中序线索二叉树,为上个函数提供Pre 
Status CreateInOrderThreadBinaryTree(ThrBiTree T, ThrBiTree* headOfTree){ 
  *headOfTree = (ThrBiTrNode*)malloc(sizeof(struct ThrBiTrNode)); 
  if( !headOfTree ) 
    return ERROR; 
  (*headOfTree)->lTag = Link;   //将要指向T 
  (*headOfTree)->rTag = Thread;    //将头节点的rchild用于索引 
  (*headOfTree)->rchild = *headOfTree;   //指向自身 
  if( T == NULL ){ 
    (*headOfTree)->lchild = *headOfTree; //若T为空树,则头节点的lchild 
              //指向自身 
  } 
  else{ 
    (*headOfTree)->lchild = T; 
    Pre = *headOfTree;     //赋值了全局变量Pre 
    InOrderThread(T);        //中序线索化 
    //printf("\n%c\n",Pre->data); 
            //执行完InOrderThread之后Pre指向最后 
            //一个节点 
    Pre->rTag = Thread;    
    Pre->rchild = *headOfTree; 
    //(*headOfTree)->rchild = Pre; 
  } 
  return OK; 
} 
//对中序线索化后的线索二叉树使用<<非递归>>的代码进行中序遍历 
//并且原来的递归形式的代码在这里是不再可以进行遍历。 
//  如果二叉树没有被线索化,也可以使用<<非递归>>的代码进行遍 
//  历的,但是那就需要借助于<<栈>>,但是在线索化之后,对线索 
//  化的二叉树进行<<非递归>>的遍历就不再需要栈的辅助。 
Status visit(ElemType c){ 
  printf("%c ", c); 
  return OK; 
} 
Status InOrderTeaverse_NoRecursion(ThrBiTree T, ThrBiTree headOfTree){ 
//headOfTree是T的头节点的指针,headOfTree->lchild = T; 
  while( T != headOfTree ){ 
    while( T->lTag == Link ){  //找到树(子树)的最左节点 
            //用while接替if// 
            //用if理解while// 
      T = T->lchild; 
    }         
    visit(T->data); 
 
    while( T->rTag == Thread && T->rchild != headOfTree){ 
            //这个while和下面的T=T->rchild 
            //可用ifelse语句理解。 
            //if(rchild是线索 && 不是最后一个节点)  
            //else(rchild不是线索)-->走到右孩子 
            //也就是代码T=T->rchild 
      T = T->rchild; 
      visit(T->data); 
    } 
    T = T->rchild; 
  } 
  return OK; 
} 
//求中序线索二叉树中中序序列下的第一个节点 
ThrBiTrNode* SeekFirstNode_InOrderThrBiTree(ThrBiTree T){ 
  if( T != NULL ){ 
    while( T->lTag == Link ){ 
      T = T->lchild; 
    } 
    return T; 
  } 
  return ERROR; 
} 
//求中序线索二叉树中节点P在中序序列下的下一个节点 
ThrBiTrNode* GetNextNode(ThrBiTrNode* CurrentP){ 
  if( CurrentP->rTag == Link ){  //有右孩子的时候,那么下一个就是以 
          //右孩子为root的树的最左下角元素。 
          //即seekFirstNode的返回值。 
    return SeekFirstNode_InOrderThrBiTree(CurrentP->rchild); 
  } 
  else{        //没有右孩子,则rchild指向后缀 
    return CurrentP->rchild; 
  } 
} 
//使用上面两个函数,SeekFirstNode_InOrderThrBiTree和GetNextNode来实现对 
//中序先做二叉树的遍历 
Status InOrderTraverse_NoRecursion_Method(ThrBiTree T, ThrBiTree head){ 
  for( T = SeekFirstNode_InOrderThrBiTree(T) ; T != head ; T = GetNextNode(T) ) 
    visit(T->data); 
  return OK; 
} 
 
//test 
/* ShowThread函数的目的是想在将T中序线索化之后,使用函数InOrderTraverse 
 * 函数中序遍历,输出一下节点中的信息以检验线索化,但是失败。原因是:在 
 * 线索化之后,空指针域都被应用,所有InOrderTraverse函数中的if( T )是满 
 * 足不了的,故失败。 
Status ShowThread(ThrBiTree C){ 
  printf("%d %d %d %d\n",(C->lchild)->data,(C->rchild)->data,C->lTag,C->rTag); 
  printf("%d %d\n",C->lTag,C->rTag); 
  return OK; 
 * */ 
 
//中序遍历二叉树 
Status InOrderTraverse(ThrBiTree T){ 
  if( T ){ 
    InOrderTraverse(T->lchild); 
    visit(T->data); 
    InOrderTraverse(T->rchild); 
  } 
  return OK; 
} 
int main(){ 
  ThrBiTree T,R,Head = NULL; 
  InitThreadBinaryTree(&T); 
 
  printf("Please Input Binary Tree By PreOrder\n  "); 
  ThreadBinaryTree_PreOrderInput(&T); 
 
  printf("  InOrder Traverse before Thread with Recursion\n"); 
  InOrderTraverse(T); 
  printf("\n"); 
 
  CreateInOrderThreadBinaryTree(T,&Head); 
  printf("  InOrder Traverse after Thread with no-Recursion\n"); 
  InOrderTeaverse_NoRecursion(T,Head); 
  printf("\n"); 
 
  printf("The root is %c \n", T->data);  //不能够通过指针形参的值改变指针实参的值 
            //或者说,对指针形参的改变不会作用到指针 
            //实参上。 
 
  ThrBiTrNode *firstOfInOrder = NULL,*secondOfInOrder = NULL; 
  if( SeekFirstNode_InOrderThrBiTree(T) != ERROR ){ 
    firstOfInOrder = SeekFirstNode_InOrderThrBiTree(T); 
    printf("the value of First Node of InOrderThreadBinaryTree is %c\n", firstOfInOrder->data); 
  } 
  secondOfInOrder = GetNextNode(firstOfInOrder); 
  printf("the value of Node after D is: %c \n", secondOfInOrder->data); 
  secondOfInOrder = GetNextNode(secondOfInOrder); 
  printf("the value of Node after B is: %c \n", secondOfInOrder->data); 
  secondOfInOrder = GetNextNode(secondOfInOrder); 
  printf("the value of Node after E is: %c \n", secondOfInOrder->data); 
  secondOfInOrder = GetNextNode(secondOfInOrder); 
  printf("the value of Node after A is: %c \n", secondOfInOrder->data); 
  secondOfInOrder = GetNextNode(secondOfInOrder); 
  printf("the value of Node after C is: %c \n", secondOfInOrder->data); 
  secondOfInOrder = GetNextNode(secondOfInOrder); 
  printf("the value of Node after G is: %c \n", secondOfInOrder->data); 
  secondOfInOrder = GetNextNode(secondOfInOrder); 
  printf("the value of Node after root is: %c \n", secondOfInOrder->data); 
 
  printf("  InOrder Traverse after Thread with no-Recursion Method_2\n"); 
  InOrderTraverse_NoRecursion_Method(T,Head); 
 
  return 0; 
} 


以上就是线索二叉树及遍历的实例,如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多