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

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;
}

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

(0)

相关推荐

  • ES6新增数据结构WeakSet的用法详解

    WeakSet和Set类似,同样是元素不重复的集合,它们的区别是WeakSet内的元素必须是对象,不能是其它类型. 特性: 1.元素必须是对象. 添加一个number类型的元素. const ws = new WeakSet() ws.add(1) 结果是报类型错误. TypeError: Invalid value used in weak set 添加一个对象. const ws = new WeakSet() var a = {p1:'1', p2:'2'} ws.add(a) conso

  • C语言数据结构 快速排序实例详解

    C语言数据结构 快速排序实例详解 一.快速排序简介 快速排序采用分治的思想,第一趟先将一串数字分为两部分,第一部分的数值都比第二部分要小,然后按照这种方法,依次对两边的数据进行排序. 二.代码实现 #include <stdio.h> /* 将两个数据交换 */ void swap(int* Ina , int* Inb) { int temp = *Ina; *Ina = *Inb; *Inb = temp; } /* 进行一趟的快速排序,把一个序列分为两个部分 */ int getPart

  • C++数据结构之文件压缩(哈夫曼树)实例详解

    C++数据结构之文件压缩(哈夫曼树)实例详解 概要: 项目简介:利用哈夫曼编码的方式对文件进行压缩,并且对压缩文件可以解压 开发环境:windows vs2013 项目概述:         1.压缩 a.读取文件,将每个字符,该字符出现的次数和权值构成哈夫曼树 b.哈夫曼树是利用小堆构成,字符出现次数少的节点指针存在堆顶,出现次数多的在堆底 c.每次取堆顶的两个数,再将两个数相加进堆,直到堆被取完,这时哈夫曼树也建成 d.从哈夫曼树中获取哈夫曼编码,然后再根据整个字符数组来获取出现了得字符的编

  • C语言数据结构之顺序数组的实现

    C语言数据结构之顺序数组的实现 以下为展示顺序数组的示例: 1.用C语言实现的版本 #include<stdio.h> /* EOF(=^Z或F6),NULL */ #include<math.h> /* floor(),ceil(),abs() */ #include<stdlib.h> /*申请和释放内存*/ #include<stdarg.h> /*可变参数*/ #define OK 1 //成功标志 #define ERROR 0 //错误标志 #d

  • C++ 数据结构之对称矩阵及稀疏矩阵的压缩存储

    对称矩阵及稀疏矩阵的压缩存储 1.稀疏矩阵 对于那些零元素数目远远多于非零元素数目,并且非零元素的分布没有规律的矩阵称为稀疏矩阵(sparse). 人们无法给出稀疏矩阵的确切定义,一般都只是凭个人的直觉来理解这个概念,即矩阵中非零元素的个数远远小于矩阵元素的总数,并且非零元素没有分布规律. 实现代码: //稀疏矩阵及其压缩存储 #pragma once #include <vector> #include <iostream> using namespace std; templa

  • C语言数据结构旋转链表的实现

    C语言数据结构旋转链表的实现 实例: 给出链表1->2->3->4->5->null和k=2 返回4->5->1->2->3->null 分析: 感觉很直观,直接把分割点找出来就行,记得k可能大于len,要取模 代码: /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x

  • C++数据结构与算法之双缓存队列实现方法详解

    本文实例讲述了C++数据结构与算法之双缓存队列实现方法.分享给大家供大家参考,具体如下: "双缓存队列"是我在一次开发任务中针对特殊场景设计出来的结构.使用场景为:发送端持续向接收端发送数据包--并且不理会接收端是否完成业务逻辑.由于接收端在任何情况下停止响应即可能产生数据丢失,因此无法简单的设计一条线程安全队列来对数据写入或读取(读取数据时将队列上锁视为对写入的停止响应). 鉴于此,我的设计思路如下: 接收端首先向A队列中写入数据,然后当数据处理请求到来的时候切换到B队列继续写入,之

  • C++数据结构与算法之反转链表的方法详解

    本文实例讲述了C++数据结构与算法之反转链表的方法.分享给大家供大家参考,具体如下: 算法概述:要求实现将一条单向链表反转并考虑时间复杂度. 算法分析: 数组法(略): 将列表元素逐个保存进数组,之后再逆向重建列表 点评:实现逻辑最简单,需要额外的内存开销. 移动指针: 通过三个指针逐个从链表头开始逐一反转链表元素的指针 点评:不需要额外的内存开销,会改变原始链表. 递归: 以递归的方式首先找到链表尾部,再逐一反转指针 点评:不需要额外的内存开销,不会改变原始链表. 算法实现: 构建链表结构 /

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

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

  • C语言数据结构系列篇二叉树的遍历

    目录 前言: Ⅰ. 定义二叉树 0x00二叉树的概念(回顾) 0x00定义二叉树 0x01 手动创建二叉树 Ⅱ. 二叉树的遍历 0x00关于遍历 0x01二叉树前序遍历 0x02二叉树中序遍历 0x03二叉树后序遍历 0x04层序遍历 前言: 学习二叉树的基本操作前,需要先创建一颗二叉树,然后才能学习其相关的基本操作,考虑到我们刚刚接触二叉树,为了能够先易后难地进行讲解,我们将暂时手动创建一颗简单的二叉树,用来方便大家学习.等二叉树结构了解的差不多后,后期我们会带大家研究二叉树地真正的创建方式.

  • C语言 数据结构之中序二叉树实例详解

    C语言数据结构 中序二叉树 前言: 线索二叉树主要是为了解决查找结点的线性前驱与后继不方便的难题.它只增加了两个标志性域,就可以充分利用没有左或右孩子的结点的左右孩子的存储空间来存放该结点的线性前驱结点与线性后继结点.两个标志性域所占用的空间是极少的,所有充分利用了二叉链表中空闲存的储空间. 要实现线索二叉树,就必须定义二叉链表结点数据结构如下(定义请看代码): left leftTag data rightTag right 说明: 1.       leftTag=false时,表示left

  • C语言数据结构详细解析二叉树的操作

    目录 二叉树分类 二叉树性质 性质的使用 二叉树的遍历 前序遍历 中序遍历 后序遍历 层序遍历 求二叉树的节点数 求二叉树叶子结点个数 求二叉树的最大深度 二叉树的销毁 二叉树分类 满二叉树 除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树.也可以理解为每一层的结点数都达到最大值的二叉树. 完全二叉树 一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下.从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为

  • C语言递归实现线索二叉树

    本文实例为大家分享了C语言递归实现线索二叉树的具体代码,供大家参考,具体内容如下 描述:将二叉树中结点的空左孩子指针域指向前驱结点,将空的右孩子指针域指向后继结点. code: #pragma warning(disable:4996) #include<stdio.h> #include<stdlib.h> typedef struct TreeNode { char data; struct TreeNode *lchild, *rchild; int ltag, rtag;

  • C语言数据结构系列篇二叉树的概念及满二叉树与完全二叉树

    链接:C语言数据结构系列之树的概念结构和常见表示方法 0x00 概念 定义:二叉树既然叫二叉树,顾名思义即度最大为2的树称为二叉树. 它的度可以为 1 也可以为 0,但是度最大为 2 . 一颗二叉树是节点的一个有限集合,该集合: ① 由一个根节点加上两颗被称为左子树和右子树的二叉树组成 ② 或者为空 观察上图我们可以得出如下结论: ① 二叉树不存在度大于 2 的节点,换言之二叉树最多也只能有两个孩子. ② 二叉树的子树有左右之分,分别为左孩子和右孩子.次序不可颠倒,因此二叉树是有序树. 注意:对

  • C语言数据结构之平衡二叉树(AVL树)实现方法示例

    本文实例讲述了C语言数据结构之平衡二叉树(AVL树)实现方法.分享给大家供大家参考,具体如下: AVL树是每个结点的左子树和右子树的高度最多差1的二叉查找树. 要维持这个树,必须在插入和删除的时候都检测是否出现破坏树结构的情况.然后立刻进行调整. 看了好久,网上各种各种的AVL树,千奇百怪. 关键是要理解插入的时候旋转的概念. // // AvlTree.h // HelloWorld // Created by feiyin001 on 17/1/9. // Copyright (c) 201

  • C语言实现线索二叉树的前中后创建和遍历详解

    目录 1.结构 1.1初始化tag 2.基本操作 2.1先序创建二叉树 2.2.先序线索化 2.2.1.先序遍历 2.3.中序线索化 2.3.1中序遍历 2.4.后序线索化 2.4.1后序遍历 总结 1.结构 #include<stdio.h> #include<stdlib.h> #define false 0 #define true 1 using namespace std; typedef struct BTnode{ int data; struct BTnode *l

  • C语言数据结构之二叉树的非递归后序遍历算法

    C语言数据结构之二叉树的非递归后序遍历算法 前言: 前序.中序.后序的非递归遍历中,要数后序最为麻烦,如果只在栈中保留指向结点的指针,那是不够的,必须有一些额外的信息存放在栈中. 方法有很多,这里只举一种,先定义栈结点的数据结构 typedef struct{Node * p; int rvisited;}SNode //Node 是二叉树的结点结构,rvisited==1代表p所指向的结点的右结点已被访问过. lastOrderTraverse(BiTree bt){ //首先,从根节点开始,

  • C语言实现线索二叉树的定义与遍历示例

    本文实例讲述了C语言实现线索二叉树的定义与遍历.分享给大家供大家参考,具体如下: #include <stdio.h> #include <malloc.h> typedef char TElemType; // 二叉树的二叉线索存储表示 typedef enum{ Link, Thread }PointerTag; // Link(0):指针,Thread(1):线索 typedef struct BiThrNode { TElemType data; struct BiThrN

随机推荐