构造中序线索二叉树和先序线索二叉树,中序线索二叉树找寻前驱结点和后继结点,包含完整代码

news2025/1/18 2:00:19

一.线索二叉树的结点结构:

lchildltagdatartagrchild

ltag = 0,   lchild域指示结点的左孩子

ltag = 1,   lchild域指示结点的前驱

rtag = 0,   rchild域指示结点的右孩子

rtag = 1,   rchild域指示结点的后继

这里用char型作为树的数据域类型

//定义线索二叉树的结构体
typedef struct ThreadNode
{
  char data;
  struct ThreadNode *lchild,*rchild;
  int ltag,rtag;
}ThreadNode,*ThreadTree;

二.如何找寻结点的前驱和后继

先将二叉树进行遍历然后根据遍历结构来找前驱和后继

以该例子的中序来表示,该二叉树的中序遍历为:BADCE

 B的前驱结点为NULL,后继结点为A

A的前驱结点为B,后继结点为D

D的前驱结点为A,后继结点为C

C的前驱结点为D,后继结点为E

E的前驱节点为C,后继结点为NULL

由上图可以看出有的结点的指针域是空的,这样可以利用这些空的指针域,若左孩子指针域为空可以指向该结点的前驱结点;若右孩子为空,可以指向该结点的后继结点

红色线表示前驱指向前驱结点,黄色线表示指向后继结点

三.二叉树线索化过程

1.先通过字符串为二叉树赋值

字符串结构体

//定义字符串的结构体
typedef struct SString
{
  char ch[Maxsize];
  int length;
}SString;

 串赋值操作

//串的赋值操作
void StrAssign(SString &S, char *chars)
{
  if(strlen(chars) > Maxsize)
	  exit(-1);
  S.length = strlen(chars);
  S.ch[0] = S.length;
  for(int i = 1; i <= S.length; ++i )
  {
	  S.ch[i] = *(chars+i-1);
  }
}

构造二叉树

//先创造一棵树,将chars元素赋值给树
void CreatTree(ThreadTree &T)
{
	char ch = S.ch[i++];
	if(ch == '#')
		T = NULL;
	else
	{
	  T = (ThreadTree)malloc(sizeof(ThreadNode));
	  if(T == NULL)
	  {
	    printf("动态内存分配失败,结束程序!\n");
		exit(-1);
	  }
	  T->data = ch;
	  CreatTree(T->lchild);
	  if(T->lchild != NULL)
		  T->ltag = 0;
	  CreatTree(T->rchild);
	  if(T->rchild != NULL)
		  T->rtag = 0;
	}
}

2.中序遍历线索二叉树

//中序遍历二叉树,一边遍历一边线索化
void InThread(ThreadTree &T)
{
  if(T != NULL)
  {
	  InThread(T->lchild);//中序遍历左子树
	  Visit(T);//访问结点
	  InThread(T->rchild);//中序遍历右子树

  }
}

访问结点函数

//访问节点
void Visit(ThreadNode *p)
{
	if(p->lchild == NULL)//如果左子树为空,建立前驱线索
	{
		p->lchild = Pre;
		p->ltag = 1;
	}
	if(Pre != NULL && p->rchild == NULL)//建立后继线索
	{
		p->rchild = Pre;
		p->rtag = 1;
	}
	Pre = p;
}

中序线索化二叉树

//中序线索化二叉树
void CreatInThread(ThreadTree &T)
{
  Pre = NULL;//初始时Pre为NULL
  if(T != NULL)//非空二叉树才能够线索化
  {
    InThread(T);//中序线索化
	if(Pre->rchild == NULL)
		Pre->rtag = 1;//处理遍历的最后一个结点
  }
}

3.找前驱结点

首先是找到以P为根结点的的子树中,最后一个被中序遍历的结点

//中序线索二叉树找中序前驱,线索二叉树中序遍历中,以P为根结点,则P的前驱肯定是在P的左子树中最右下结点
//找到以P为根结点的的子树中,最后一个被中序遍历的结点
ThreadNode *LastNode(ThreadNode *P)
{
	while(P->rtag == 0)
		P = P->rchild;
	return P;
}

左子树最右下结点

//在中序线索二叉树中找到结点P的前驱结点
ThreadNode *Prenode(ThreadNode *P)
{//左子树中最右下结点
	if(P->ltag == 0)
		return LastNode(P->lchild);
	else
		return P->lchild;
}

4.找后继结点

找左子树最左下结点

//中序线索二叉树找中序后继,在中序线索二叉树中,若是找根结点的后继节点,那么肯定是右子树中第一个遍历的结点
//右子树的最左下结点
ThreadNode *firstnode(ThreadNode *P)
{
	while(P->ltag == 0)
		P = P->lchild;
	return P;
}

右子树最左下结点 

//在中序遍历的线索二叉树中,以P为根节点,找P结点的中序遍历后继结点
ThreadNode *nextnode(ThreadNode *P)
{//右子树最左下结点
	if(P->rtag == 0)
		return firstnode(P->rchild);
	else
		return P->rchild;
}

结果

 5.二叉树先序遍历线索化

//先序遍历线索化,一边遍历一边线索化
void PreThread(ThreadTree T)
{
  if(T != NULL)
  {
    Visit(T);//先访问根结点
	if(T->ltag == 0)//防止因为线索而导致一直循环
		PreThread(T->lchild);//访问左子树
	PreThread(T->rchild);//访问右子树
  }
}

//创造先序线索化二叉树
void CreatPreThread(ThreadTree T)
{
  Pre = NULL;
  if(T != NULL)
  {
    PreThread(T);
	if(Pre->rchild == NULL)
		Pre->rtag = 1;//处理最后一个结点
  }
}

完整代码

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>

#define Maxsize 11

//定义线索二叉树的结构体
typedef struct ThreadNode
{
  char data;
  struct ThreadNode *lchild,*rchild;
  int ltag,rtag;
}ThreadNode,*ThreadTree;
//定义一个全局变量
ThreadNode *Pre = NULL;

//定义字符串的结构体
typedef struct SString
{
  char ch[Maxsize];
  int length;
}SString;

//定义一个全局变量的字符串
SString S;
int i = 1;

//串的函数说明
void StrAssign(SString &S, char *chars);

//树的函数说明
void CreatTree(ThreadTree &T);
void InThread(ThreadTree &T);
void Visit(ThreadNode *p);
void CreatInThread(ThreadTree &T);
ThreadNode *LastNode(ThreadNode *P);
ThreadNode *Prenode(ThreadNode *P);
ThreadNode *firstnode(ThreadNode *P);
ThreadNode *nextnode(ThreadNode *P);

int main(void)
{
  ThreadTree T;
  StrAssign(S,"AB##CD##E##");
  CreatTree(T);
  ThreadNode *V1 = Prenode(T);
  ThreadNode *V2 = nextnode(T);
  printf("根结点的前驱结点的值为:%c\n",V1->data);
  printf("根结点的后继结点的值为:%c\n",V2->data);

  return 0;
}

//串的赋值操作
void StrAssign(SString &S, char *chars)
{
  if(strlen(chars) > Maxsize)
	  exit(-1);
  S.length = strlen(chars);
  S.ch[0] = S.length;
  for(int i = 1; i <= S.length; ++i )
  {
	  S.ch[i] = *(chars+i-1);
  }
}

//先创造一棵树,将chars元素赋值给树
void CreatTree(ThreadTree &T)
{
	char ch = S.ch[i++];
	if(ch == '#')
		T = NULL;
	else
	{
	  T = (ThreadTree)malloc(sizeof(ThreadNode));
	  if(T == NULL)
	  {
	    printf("动态内存分配失败,结束程序!\n");
		exit(-1);
	  }
	  T->data = ch;
	  CreatTree(T->lchild);
	  if(T->lchild != NULL)
		  T->ltag = 0;
	  CreatTree(T->rchild);
	  if(T->rchild != NULL)
		  T->rtag = 0;
	}
}


//中序遍历二叉树,一边遍历一边线索化
void InThread(ThreadTree &T)
{
  if(T != NULL)
  {
	  InThread(T->lchild);//中序遍历左子树
	  Visit(T);//访问结点
	  InThread(T->rchild);//中序遍历右子树

  }
}

//访问节点
void Visit(ThreadNode *p)
{
	if(p->lchild == NULL)//如果左子树为空,建立前驱线索
	{
		p->lchild = Pre;
		p->ltag = 1;
	}
	if(Pre != NULL && p->rchild == NULL)//建立后继线索
	{
		p->rchild = Pre;
		p->rtag = 1;
	}
	Pre = p;
}

//中序线索化二叉树
void CreatInThread(ThreadTree &T)
{
  Pre = NULL;//初始时Pre为NULL
  if(T != NULL)//非空二叉树才能够线索化
  {
    InThread(T);//中序线索化
	if(Pre->rchild == NULL)
		Pre->rtag = 1;//处理遍历的最后一个结点
  }
}

//中序线索二叉树找中序前驱,线索二叉树中序遍历中,以P为根结点,则P的前驱肯定是在P的左子树中最右下结点
//找到以P为根结点的的子树中,最后一个被中序遍历的结点
ThreadNode *LastNode(ThreadNode *P)
{
	while(P->rtag == 0)
		P = P->rchild;
	return P;
}

//在中序线索二叉树中找到结点P的前驱结点
ThreadNode *Prenode(ThreadNode *P)
{//左子树中最右下结点
	if(P->ltag == 0)
		return LastNode(P->lchild);
	else
		return P->lchild;
}



//中序线索二叉树找中序后继,在中序线索二叉树中,若是找根结点的后继节点,那么肯定是右子树中第一个遍历的结点
//右子树的最左下结点
ThreadNode *firstnode(ThreadNode *P)
{
	while(P->ltag == 0)
		P = P->lchild;
	return P;
}

//在中序遍历的线索二叉树中,以P为根节点,找P结点的中序遍历后继结点
ThreadNode *nextnode(ThreadNode *P)
{//右子树最左下结点
	if(P->rtag == 0)
		return firstnode(P->rchild);
	else
		return P->rchild;
}


//先序遍历线索化,一边遍历一边线索化
void PreThread(ThreadTree T)
{
  if(T != NULL)
  {
    Visit(T);//先访问根结点
	if(T->ltag == 0)//防止因为线索而导致一直循环
		PreThread(T->lchild);//访问左子树
	PreThread(T->rchild);//访问右子树
  }
}

//创造先序线索化二叉树
void CreatPreThread(ThreadTree T)
{
  Pre = NULL;
  if(T != NULL)
  {
    PreThread(T);
	if(Pre->rchild == NULL)
		Pre->rtag = 1;//处理最后一个结点
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/678286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能python:Python用于股票:掌握数据、分析趋势

Python用于股票&#xff1a;掌握数据、分析趋势 在当今数字化时代&#xff0c;投资者使用数据分析技术作出投资决策变得越来越重要&#xff0c;而Python正是一种无形中帮助投资者进行数据分析的强有力工具。Python是一种高级数据分析语言&#xff0c;具有易读易懂的语法和强大…

牛客网专项练习——C语言错题集(9)

文章目录 字符常量\0 与 逻辑假的关系不同类型变量所占空间大小运行多个 fork容易造成缓冲区溢出的字符串操作函数函数原型格式指针变量的关系运算&#xff0c;空指针访问存储单元BSS 段二维数组初始化 字符常量 ‘a’ 是一个字符常量&#xff0c;占用 1 个字节空间。 ‘\0’ …

数据结构期末复习【更新】

数据结构期末复习【更新】 1.模式匹配2.画二叉树&#xff08;根据中序和后序&#xff0c;前序和中序&#xff09;及其线索二叉树3.求叶子结点个数4.建立二叉排序树5.广义表6.求存储地址7.代码设计8.哈夫曼树9.最小生成树10.深度遍历、广度遍历、邻接表建立11.哈希表&#xff08…

【Leetcode60天带刷】day28回溯算法——93.复原IP地址 ,78.子集 , 90.子集II

​ 题目&#xff1a; 地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址&#xf…

【Leetcode60天带刷】day22二叉树—— 235. 二叉搜索树的最近公共祖先 ,701.二叉搜索树中的插入操作 ,450.删除二叉搜索树中的节点

​ 题目&#xff1a; 235. 二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表示为一个结点 x&#xff0c;满足 x 是 p、q 的祖先…

【Leetcode60天带刷】day23二叉树—— 669. 修剪二叉搜索树 ,108.将有序数组转换为二叉搜索树 , 538.把二叉搜索树转换为累加树

​ 题目&#xff1a; 669. 修剪二叉搜索树 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有被移除&…

高速板材应用案例

要求如下: 高速信号部分有12.5Gbps Interlaken信号、QPI、PCIe3.0信号(后面有兼容PCIe4.0的要求),另外还有10.3125Gbps到光口信号;高速信号损耗要求: -0.8dB/inch@4GHz,-1.6dB/inch@8GHz差分信号阻抗控制有85ohm、90ohm和100ohm,单端按照50ohm控制,阻抗及线宽控制表如下…

【Leetcode60天带刷】day29回溯算法——491.递增子序列, 46.全排列, 47.全排列 II

​ 题目&#xff1a; 491. 递增子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递…

基于免疫优化算法的线性规划问题求解(matlab程序)

0.代码链接 基于免疫优化算法的线性规划问题求解&#xff08;matlab程序&#xff09;资源-CSDN文库 1.简述 免疫算法&#xff08;Immune Algorithm&#xff0c;IA&#xff09;&#xff1a;是指以在人工免疫系统的理论为基础&#xff0c;实现了类似于生物免疫系统的抗原识别…

【LeetCode】动态规划 刷题训练(二)

文章目录 62. 不同路径题目解析状态转移方程完整代码 63. 不同路径 II题目解析状态转移方程完整代码 剑指 Offer 47. 礼物的最大价值题目解析状态转移方程完整代码 62. 不同路径 点击查看&#xff1a;不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图…

树莓派与STM32之间串口通信

目录 一、树莓派串口通信模块介绍 二、树莓派蓝牙、串口引脚映射对换步骤 1.启动串口 2. 禁用蓝牙&#xff08;硬件串口与mini串口映射对换&#xff09; 3.验证是否交换成功 三、树莓派安装mini串口调试助手 四、树莓派与电脑串口调试 五、树莓派与STM32串口调试 一、树…

Stable-Diffusion-webui mac m1安装

Stable-Diffusion-webui mac m1安装 推荐下载器&#xff1a;neat download manager 安装git python 3.10 下载地址&#xff1a;https://github.com/AUTOMATIC1111/stable-diffusion-webui 我这边是通过pycharm 通过git直接拉取到本地的&#xff0c;下载的&#xff0c;最好先建…

使用AI轻松搞定UI设计;a16z:快速高效使用LLM构建应用程序;AI时代99%软件都会消失;豆瓣9.3的经典Python入门书 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; Indeed&#xff1a;美国5月份生成式AI职位发布量增长 20% Indeed 是美国就业门户网站&#xff0c;根据其最新发布的数据显示&#xff…

Golang -> Golang 的概述

Golang 的概述 Golang 的语言的特点Golang 的开发工具Golang 的版本 Golang 的语言的特点 简介&#xff1a; Go 语言保证了既能到达静态编译语言的安全和性能&#xff0c;又达到了动态语言开发维护的高效率 使用一个表达式来形容 Go 语言&#xff1a;Go C Python 说明 Go …

书虫“Bookworm”出山:Debian 版本代号与《玩具总动员》

作为最受欢迎的 Linux 发行版之一&#xff0c;Debian 是许多其他发行版的基础&#xff0c;许多非常受欢迎的 Linux 发行版&#xff0c;例如 Ubuntu、Knoppix、PureOS 、Tails、Armbian 以及 Raspbian&#xff0c;都基于 Debian。 经过近 20 个月的开发&#xff0c;2023 年 6 月…

VMware vCenter Server 8.0U1b 发布下载(重要安全更新)

VMware vCenter Server 8.0U1b 发布下载&#xff08;重要安全更新&#xff09; 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-vcenter-8-u1/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org VMware vCenter Serve…

【数据网格架构】数据网格架构模式

企业数据网格正在彻底改变企业管理数据的方式。什么是基础数据网格模式&#xff1f; 数据网格模式 企业数据网格正在成为一种独特且引人注目的方式来管理企业内的数据。它将“产品思维”引入企业数据管理&#xff0c;同时在企业中实现更高水平的敏捷性和数据治理。它创造了一种…

行为型模式--迭代器模式

目录 概述 结构 案例实现 优缺点 优点&#xff1a; 缺点&#xff1a; 使用场景 概述 提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象的内部表示。 结构 迭代器模式主要包含以下角色&#xff1a; 抽象聚合&#xff08;Aggregate&#xf…

【裸机开发】IRQ 中断服务函数(二)—— 全局中断初始化

实现了 IRQ 中断服务函数的汇编部分以后&#xff0c;接下来我们要使用C代码实现IRQ中断服务函数的具体逻辑&#xff0c;主要包含初始化和中断处理两部分。 全局中断初始化&#xff08;全局中断使能、IRQ中断使能&#xff09;具体中断处理逻辑实现 目录 一、全局中断初始化&am…

【C++】第14章: C++中的代码重用

第十四章 C中的代码重用 文章目录 第十四章 C中的代码重用引言14.1 valarray类简介14.1.1 接口和实现14.1.2 C和约束14.1.3 初始化顺序 14.2 has a14.2.1 组合14.2.2 私有继承14.2.2.1 访问基类的方法14.2.2.2 访问基类对象14.2.2.3 访问基类的友元函数 14.2.3 使用组合还是私有…