【数据结构】-线索二叉树(中序)

淩亂°似流年 2023-03-03 08:55 180阅读 0赞

中序线索化二叉树

  • 1.头文件及类型定义
  • 2.线索二叉树结点类型定义
  • 3.函数声明
  • 4.基本操作
    • 4.1 先序建立线索二叉树
    • 4.2 初始化tag
    • 4.3 中序线索化二叉树
      • 4.3.1 访问并建立线索
      • 4.3.2 遍历
      • 4.3.3 中序线索化主过程
    • 4.4 寻找中序后继
      • 4.4.1 找到最左下结点
      • 4.4.2 找到p的后继结点
    • 4.4 寻找中序前驱
      • 4.4.1 找到最右下结点
      • 4.4.2 找到p的前驱
    • 4.5 中序遍历和逆向中序遍历
      • 4.5.1 打印结点
      • 4.5.2 利用中序后继实现中序遍历
      • 4.5.3 利用中序前驱实现逆向中序遍历
    • 4.6 main函数
    • 4.7 测试
      • 4.7.1 二叉树结构
      • 4.7.2 测试结果
  • 5.小结

1.头文件及类型定义

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define ElemType char

2.线索二叉树结点类型定义

  1. //线索二叉树结点类型定义
  2. typedef struct ThreadNode {
  3. ElemType data; //数据元素
  4. struct ThreadNode* lchild, * rchild; //左、右孩子指针
  5. int ltag, rtag; //左、右线索标志
  6. //tag=0,表示指针指向孩子;tag=1,表示指针是“线索”,ltag指向前驱,rtag指向后继
  7. }ThreadNode, * ThreadTree;

3.函数声明

  1. /*函数声明*/
  2. void CreateThTree(ThreadTree& T); //1.先序建立线索二叉树
  3. void InitThread(ThreadTree& T); //2.初始化tag
  4. void visit1(ThreadNode* q); //3-1.访问并建立线索
  5. void InThread(ThreadTree T); //3-2.遍历
  6. void CreateInThread(ThreadTree T); //3-3.中序线索化主过程
  7. ThreadNode* FirstNode(ThreadNode* p); //4-1.找到以p为根的子树中,第一个被中序遍历的结点
  8. ThreadNode* NextNode(ThreadNode* p); //4-2.找到p的后继结点
  9. ThreadNode* LastNode(ThreadNode* p); //5-1.找到以p为根的子树中,最后一个被中序遍历的结点
  10. ThreadNode* PreNode(ThreadNode* p); //5-2.找到p的前驱结点
  11. void visit2(ThreadNode* p); //6-1.打印结点
  12. void InOrder(ThreadNode* T); //6-2.利用中序后继实现中序遍历
  13. void RevInOrder(ThreadNode* T); //6-3.利用中序前驱实现逆向中序遍历

4.基本操作

4.1 先序建立线索二叉树

  1. /*1.先序建立线索二叉树*/
  2. void CreateThTree(ThreadTree& T) {
  3. char c;
  4. scanf("%c", &c);
  5. if (c == '#')
  6. T = NULL;
  7. else {
  8. T = (ThreadNode*)malloc(sizeof(ThreadNode));
  9. T->data = c;
  10. CreateThTree(T->lchild);
  11. CreateThTree(T->rchild);
  12. }
  13. }

4.2 初始化tag

  1. /*2.初始化tag*/
  2. void InitThread(ThreadTree& T) {
  3. if (T != NULL) {
  4. T->ltag = 0;
  5. T->rtag = 0; //初始化当前树中的tag指针为0,表示还未线索化
  6. InitThread(T->lchild); //递归遍历左子树
  7. InitThread(T->rchild); //递归遍历右子树
  8. }
  9. }

4.3 中序线索化二叉树

4.3.1 访问并建立线索

  1. ThreadNode* pre = NULL; //pre指向当前访问结点的前驱
  2. //3-1.访问并建立线索
  3. void visit1(ThreadNode* q) {
  4. if (q->lchild == NULL) {
  5. //左子树为空,建立前驱线索
  6. q->lchild = pre;
  7. q->ltag = 1;
  8. }
  9. if (pre != NULL && pre->rchild == NULL) {
  10. pre->rchild = q; //建立前驱结点的后继线索
  11. pre->rtag = 1;
  12. }
  13. pre = q; //标记当前结点为刚刚访问过的结点
  14. }

4.3.2 遍历

  1. //3-2.遍历
  2. void InThread(ThreadTree T) {
  3. if (T != NULL) {
  4. InThread(T->lchild); //中序遍历左子树
  5. visit1(T); //访问根节点
  6. InThread(T->rchild); //中序遍历右子树
  7. }
  8. }

4.3.3 中序线索化主过程

  1. //3-3.主过程
  2. void CreateInThread(ThreadTree T) {
  3. pre = NULL; //pre初始化为NULL
  4. if (T != NULL) {
  5. //非空二叉树才能线索化
  6. InThread(T); //中序线索化二叉树
  7. if (pre->rchild == NULL) //实际上不用判断,中序遍历时最后一个结点的右孩子指针必为NULL
  8. pre->rtag = 1; //处理遍历的最后一个结点
  9. }
  10. }

4.4 寻找中序后继

4.4.1 找到最左下结点

  1. //4-1.求中序线索二叉树中中序遍历的第一个结点:右子树中最左下的结点
  2. ThreadNode* FirstNode(ThreadNode* p) {
  3. //循环找到最左下结点(不一定是叶节点)
  4. while (p->ltag == 0)
  5. p = p->lchild;
  6. return p;
  7. }

4.4.2 找到p的后继结点

  1. //4-2.找到p的后继结点
  2. ThreadNode* NextNode(ThreadNode* p) {
  3. if (p->rtag == 0) //若rtag=0,说明所找结点有右孩子,则找该结点的右子树最左下结点
  4. return FirstNode(p->rchild);
  5. else
  6. return p->rchild; //若rtag=1,说明所找结点无右孩子,则返回后继线索
  7. }

4.4 寻找中序前驱

4.4.1 找到最右下结点

  1. //5-1.求中序线索二叉树中中序遍历的最后一个结点:左子树中最右下的结点
  2. ThreadNode* LastNode(ThreadNode* p) {
  3. //循环找到最右下结点(不一定是叶节点)
  4. while (p->rtag == 0)
  5. p = p->rchild;
  6. return p;
  7. }

4.4.2 找到p的前驱

  1. //5-2.找到p的前驱结点
  2. ThreadNode* PreNode(ThreadNode* p) {
  3. if (p->ltag == 0) //若ltag=0,说明所找结点有左孩子,则找该结点的左子树最右下结点
  4. return LastNode(p->lchild);
  5. else
  6. return p->lchild; //若ltag=1,说明所找结点无左孩子,则返回前驱线索
  7. }

4.5 中序遍历和逆向中序遍历

4.5.1 打印结点

  1. //6-1.打印结点
  2. void visit2(ThreadNode* p) {
  3. printf("%c\t", p->data);
  4. }

4.5.2 利用中序后继实现中序遍历

  1. //6-2.利用中序后继实现中序遍历:空间复杂度为O(1)
  2. void InOrder(ThreadNode* T) {
  3. for (ThreadNode* p = FirstNode(T); p != NULL; p = NextNode(p))
  4. visit2(p);
  5. }

4.5.3 利用中序前驱实现逆向中序遍历

  1. //6-2.利用中序后继实现中序遍历:空间复杂度为O(1)
  2. void RevInOrder(ThreadNode* T) {
  3. for (ThreadNode* p = LastNode(T); p != NULL; p = PreNode(p))
  4. visit2(p);
  5. }

4.6 main函数

  1. int main() {
  2. ThreadTree T;
  3. /*1、创建二叉树*/
  4. printf("先序创建二叉树:");
  5. CreateThTree(T);
  6. /*2、初始化tag为0*/
  7. InitThread(T);
  8. /*3、中序线索化*/
  9. CreateInThread(T);
  10. /*4、寻找中序后继(用根结点测试)*/
  11. printf("根结点的中序后继为:%c\n", NextNode(T)->data);
  12. /*5、寻找中序前驱(用根结点测试)*/
  13. printf("根结点的中序前驱为:%c\n", PreNode(T)->data);
  14. /*6、中序遍历二叉树*/
  15. printf("<————中序遍历————>\n");
  16. InOrder(T);
  17. /*7、逆向中序遍历二叉树*/
  18. printf("\n<————逆向中序遍历————>\n");
  19. RevInOrder(T);
  20. return 0;
  21. }

4.7 测试

4.7.1 二叉树结构

在这里插入图片描述

4.7.2 测试结果

在这里插入图片描述

5.小结

  1. 关于线索二叉树
    二叉树的线索化是将二叉链表中的n+1个空指针改为指向前驱或后继的线索。但前驱或后继的信息只有在遍历时才能得到,因此,线索化二叉树的本质是二叉树的遍历。
  2. 在对二叉树进行线索化以后,就可以很方便的找到指定结点的前驱或者后继,并且,利用寻找中序前驱或者中序后继的操作可以很方便的实现二叉树的中序遍历和逆向中序遍历,且这样实现的遍历空间复杂度仅为O(1)。

发表评论

表情:
评论列表 (有 0 条评论,180人围观)

还没有评论,来说两句吧...

相关阅读