数据结构-树与二叉树

news2024/9/27 7:43:16

文章目录

  • 5.1 树的基本概念
    • 5.1.1 树的定义
    • 5.1.2 基本术语
    • 5.1.3 树的性质
  • 5.2 二叉树的概念
    • 5.2.1 二叉树的定义及其主要特性
    • 5.2.2 二叉树的存储结构
  • 5.3 二叉树的遍历和线索二叉树
    • 5.3.3 二叉树的遍历
    • 5.3.2 线索二叉树
  • 5.4 树、森林
    • 5.4.1 树的存储结构
    • 5.4.2 树、森林、二叉树的转换
    • 5.4.3 树和森林的遍历
  • 5.5 树和二叉树的应用
    • 5.5.1 哈夫曼树和哈夫曼编码

5.1 树的基本概念


5.1.1 树的定义

🎈树是n(n>=0)个结点的有限集。当n=0时,称为空树。
特点:

  • 树的根节点没有前驱,除根节点外的所有结点有且只有一个前驱
  • 树中所有结点都可以有零个或多个后继

5.1.2 基本术语

✅结点

根结点: 树只有一个根节点

结点的度: 一个结点的子节点的个数,度为0则该结点为叶子结点(终端结点),度不为0则该结点为分支结点(非终端结点)

结点的关系: 祖先结点、子孙结点、双亲结点(父亲结点)、孩子结点、兄弟结点、堂兄弟结点

结点的高度: 从叶子结点开始自底向上逐层累加

结点的深度: 从根结点开始自顶向下逐层累加

层次: 从树根开始,根结点为第一层

✅树

树的度: 树中结点最大的度

树的高度/层次: 树中结点的最大层次

有序树和无序树: 树中结点的各子树从左到右是有次序的,不能互换,称该树为有序树,否则称为无序树

路径和路径长度: 树中两个结点之间的路径是由这两个结点之间所经过的结点序列构成的,而路径长度是路径上所经过的边的个数

5.1.3 树的性质

  1. 树中的结点数等于所有结点的度数之和加1
  2. 度为m的树中第i层上至多有 m i-1 个结点
  3. 高度为h的m叉树至多有 (mh-1)/(m-1) 个结点
  4. 具有n个结点的m叉树的最小高度为 [logm(n(m-1)+1)] (向上取整)

5.2 二叉树的概念


5.2.1 二叉树的定义及其主要特性

✅二叉树的定义

🎈每个结点至多有两颗子树,并且二叉树的结点有左右之分

🎄二叉树的五种形态
在这里插入图片描述

特殊二叉树

🔴满二叉树:除叶子结点外,每个结点度数均为2

🟠完全二叉树:与其对应满二叉树中编号1~n一 一对应 度为1的结点只能有一个,且该结点只有左孩子没有右孩子

🟡二叉排序树:左子树的关键字 < 根 < 右子树的关键字

🔵平衡二叉树:树上任意结点的左子树和右子树的深度之差不超过1

二叉树的性质

①完全二叉树:

  • 若i≤ [n/2](向下取整),则结点i为分支结点,否则为叶子结点
  • 叶子结点只可能在层次最大的两层出现。对于最大层次中的叶子结点,都一次排列在该层最左边的位置上若有度为1的结点,则只能有一个,且该结点只有左孩子而无右孩子(重要特征)
  • 按层序编号后,一旦出现某结点(编号为i)为叶子结点或只有左孩子,则编号大于i的结点均为叶子结点
  • 若n为奇数,则每个分支结点都有左孩子和右孩子;
  • 若n为偶数,则编号最大的分支结点(编号为n/2)只有左孩子,没有右孩子,其余分支结点左、右孩子都有

在这里插入图片描述

②非空二叉树上的叶子结点数=度为2的结点数+1 n0 = n2 + 1

③非空二叉树上第k层上至多有2(k-1)个结点(k≥1)

④高度为h的二叉树至多有2h-1个结点(h≥1)

⑤对于完全二叉树按从上到下、从左到右的顺序一次编号1,2…n,则有以下关系:

  • 当i >1时,结点i的双亲的编号为[i/2](向下取整),即当i为偶数时,其双亲的编号为i/2,它是双亲的左孩子;当i为奇数时,其双亲的编号为(i-1)/2,它是双亲的右孩子
  • 当2i≤n时,结点i的左孩子编号为2i,否则无左孩子
  • 3当2i+1≤ n时,结点i的右孩子编号为2i+1,否则无右孩子
  • 结点i所在层次(深度)为[logzi]+1(向下取整)

5.2.2 二叉树的存储结构

顺序存储结构

🎈采用一组地址连续的存储单元依次自上而下、自左向右存储完全二叉树上的结点元素
适合存储完全二叉树和满二叉树

链式存储结构

🎈由于顺序存储的空间利用率比较低,因此一般采用链式存储结构
数据域(data) ,左指针域(Ichild),右指针域(rchild)
重要结论:含有n个结点的二叉链表中,含有n+1个空链域

5.3 二叉树的遍历和线索二叉树


5.3.3 二叉树的遍历

先序遍历
若二叉树为空,则什么也不做;否则:

  1. 访问根结点
  2. 先序遍历左子树
  3. 先序遍历右子树
void PreOrder(BiTree T){
	if(T != NULL){
		visit(T);
		PreOrder(T->lchild);
		PreOrder(T->rchild);
	}
}

中序遍历
若二叉树为空,则什么也不做;否则:

  1. 中序遍历左子树
  2. 访问根结点
  3. 中序遍历右子树
void InOrder(BiTree T){
	if(T != NULL){
		InOrder(T->lchild);
		visit(T);
		InOrder(T->rchild);
	}
}

后序遍历
若二叉树为空,则什么也不做;否则:

  1. 后序遍历左子树
  2. 后序遍历右子树
  3. 访问根结点
void PostOrder(BiTree T){
	if(T != NULL){
		PostOrder(T->lchild);
		PostOrder(T->rchild);
		visit(T);
	}
}

层序遍历

  1. 初始化一个辅助队列
  2. 根结点入队
  3. 若队列非空,则队头结点出队,访问该结点,并将其左、右孩子插入队尾(如果有的话)
  4. 重复3直至队列为空
void LevelOrder(BiTree T){
	LinkQueue Q;
	InitQueue(Q);                 //初始化辅助队列       
	BiTree p;
	EnQueue(Q, p);                //将根结点入队
	while(!IsEmpty(Q)){           //队列不空则循环
		DeQueue(Q, p);            //队头结点出队
		visit(p);                 //访问出队结点
		if(p->lchild!=NULL)
			EnQueue(Q,p->lchild); //左孩子入队
		if(p->rchild!=NULL)
			EnQueue(Q,p->rchild); //右孩子入队
	}
}

由遍历序列构造二叉树

🎄前序+中序序列

在这里插入图片描述

🎄后序+中序序列
在这里插入图片描述

🎄层序+中序序列
在这里插入图片描述

5.3.2 线索二叉树

线索二叉树的基本概念
🎈二叉树的线索化是指将二叉链的空指针改为指向前驱或后继的线索。若无左子树,令Ichilid指向其前驱结点;若无右子树,令rchlid指向其后继结点

在这里插入图片描述
增加两个标志域表面当前指针域所指对象是左右结点还是直接前驱或后继
ltag:0表示lchild指向结点的左孩子,1表示lchild指向结点的前驱
rtag:0表示rchild指向结点的右孩子,1表示rchild指向结点的后继

线索二叉树的构造

🎄中序线索化
在这里插入图片描述

void InThread(ThreadTree &p,ThreadTree &pre){
	if(p!=NULL){
		InThread(p->lchild,pre);                //递归,线索化左子树
		if(p->lchild==NULL){                    //左子树为空,建立前驱线索
			p->lchild=pre;
			p->ltag=1;
		}
		if(pre!=NULL&&pre->rchild==NULL){
			pre->rchild=p;                     //建立前驱结点的后继线索
			pre->rtag=1;
		}
		pre=p;
		InThread(p->rchild,pre);               //递归,线索化右子树
	}
}


void CreateInThread(TreadTree T){
	ThreadTree pre=NULL;
	if(T!=NULL){
		InThread(T,pre);                  //线索化二叉树
		pre->rchild=NULL;                 //处理遍历的最后一个结点
		pre->rtag=1;
	}
}

🎄先序线索化

void PreThread(ThreadTree &p,ThreadTree &pre){
	if(p!=NULL){              
		if(p->lchild==NULL){                    //左子树为空,建立前驱线索
			p->lchild=pre;
			p->ltag=1;
		}
		if(pre!=NULL&&pre->rchild==NULL){
			pre->rchild=p;                     //建立前驱结点的后继线索
			pre->rtag=1;
		}
		pre=p;
		if(p->ltag==0)
			PreThread(p->lchild,pre);           //递归,线索化左子树
		PreThread(p->rchild,pre);               //递归,线索化右子树
	}
}


void CreatePreThread(TreadTree T){
	ThreadTree pre=NULL;
	if(T!=NULL){
		PreThread(T,pre);                  //线索化二叉树
		if(pre->rchild==NULL)              //处理遍历的最后一个结点
			pre->rtag=1;
	}
}

🎄后序线索化

void PostThread(ThreadTree &p,ThreadTree &pre){
	if(p!=NULL){          
		PostThread(p->lchild,pre);               //递归,线索化左子树
		PostThread(p->rchild,pre);               //递归,线索化右子树    
		if(p->lchild==NULL){                    //左子树为空,建立前驱线索
			p->lchild=pre;
			p->ltag=1;
		}
		if(pre!=NULL&&pre->rchild==NULL){
			pre->rchild=p;                     //建立前驱结点的后继线索
			pre->rtag=1;
		}
		pre=p;
	}
}


void CreatePostThread(TreadTree T){
	ThreadTree pre=NULL;
	if(T!=NULL){
		PostThread(T,pre);                  //线索化二叉树
		if(pre->rchild==NULL)              //处理遍历的最后一个结点
			pre->rtag=1;
	}
}

线索二叉树找前驱后继

若Itag/rtag==0

  • 中序线索二叉树

    • 后继:p的右子树中最左下结点
    • 前驱:p的左子树中最右下结点
  • 先序线索二叉树

    • 后继:

      • 若p有左孩子,则先序后继为左孩子
      • 回若p没有左孩子,则先序后继为右孩子
    • 前驱:

      • 如果能找到p的父节点,且p是左孩子,则p的父节点为其前驱
      • 如果能找到p的父节点,且p是右孩子,其左兄弟为空,则p的父节点即为其前驱
      • 如果能找到p的父节点,且p是右孩子,其左兄弟非空,则p的前驱为其左兄弟子树最后一个被先序遍历的结点
      • 如果p是根节点,则p没有先序前驱
  • 后序线索二叉树

    • 后继:

      • 如果能找到p的父节点,且p是右孩子,p的父节点即为其后继
      • 如果能找到p 的父节点,且p是左孩子,其右兄弟为空,p的父节点即为其后继
      • 如果能找到p的父节点,且p是左孩子,其右兄弟非空,则p的后继为其右兄弟子树中第一个被后序遍历的结点
      • 如果p是根节点,则p没有后序后继
    • 前驱:

      • 若p有右孩子,则后序前驱为右孩子
      • 若p没有右孩子,则后序前驱为左孩子

在这里插入图片描述
后序线索树的遍历仍需要栈的支持
请添加图片描述
后序线索树遍历时,最后访问根结点,若从右孩子x返回访问父结点,则由于结点x的右孩子不一定为空(右指针无法指向其后继),因此通过指针可能无法遍历整棵树。

5.4 树、森林


5.4.1 树的存储结构

双亲表示法

🎈增设尾指针,指示其双亲结点在数组中的位置。根结点下标为0,伪指针为-1

优点: 快速查找双亲结点 O(1)
缺点: 求孩子时需要遍历整个结构

#define MAX_TREE_SIZE 100
typedef struct{
	ElemType data;
	int parent;
}PTNode;
typedef struct{
	PTNode nodes[MAX_TREE_SIZE];
	int n;
}PTree;

孩子表示法

🎈n个结点就有n个孩子链表((叶子结点的孩子链表为空表),n个结点就有n个孩子链表((叶子结点的孩子链表为空表)
在这里插入图片描述
优点: 易于查找孩子结点
缺点: 查找双亲麻烦,需要遍历n个结点中孩子链表指针所指向的n个孩子链表

struct CTNode{
	int child;                  //孩子结点在数组中的位置
	struct CTNode *next;        //下一个孩子
};
typedef struct{
	ElemType data;
	struct CTNode *firstChild;   //第一个孩子
}CTBox;
typedef struct{
	CTBox nodes[MAX_TREE_SIZE];
	int n,r;
}CTree;

孩子兄弟表示法

🎈设置两个指针,一个指向结点的第一个孩子结点,另一个指向该结点的下一个兄弟结点

优点: 易于查找孩子结点和兄弟,可以方便地实现树转换为二叉树地操作
缺点: 查找双亲麻烦

typedef struct CSNode{
	ElemType data;
	struct CSNode *firstchild,*nextsibling;
}CSNode,*CSTree;

5.4.2 树、森林、二叉树的转换

树和二叉树的转换

每个结点左指针指向它的第一个孩子结点,右指针指向它在树中的相邻兄弟结点
在这里插入图片描述
二叉树和森林的转换

森林====>二叉树

  1. 将森林中的每棵树转换成相应的二叉树

  2. 每棵树的根也可视为兄弟关系,在每棵树的根之间加一根连线

  3. 以第一棵树的根为轴心顺时针旋转45°
    在这里插入图片描述
    二叉树====>森林

    1)若二叉树非空,则二叉树的根及其左子树为第一棵树的二叉树形式,故将根的右链断开。
    2)二叉树根的右子树又可视为—一个由除第一棵树外的森林转换后的二叉树,应用同样的方法,直到最后只剩一棵没有右子树的二叉树为止
    3)最后再将每棵二叉树依次转换成树,就得到了原森林。二叉树转换为树或森林是唯一的
    在这里插入图片描述

5.4.3 树和森林的遍历

树的遍历

先根遍历: 若树非空,先访问根结点,再从左到右的顺序遍历根结点的每棵子树

后根遍历: 若树非空,按从左到右的顺序遍历根节点的每棵子树,再访问根节点、

树的先根遍历==转化成的二叉树先序遍历

树的后根遍历==转化成的二叉树中序遍历

森林的遍历

先序遍历:

  • 访问森林中第一棵树的根结点
  • 先序列遍历第一棵树中根结点的子树森林
  • 先序遍历除去第一棵树之后剩余的树构成的森林

中序遍历:

  • 中序列遍历第一棵树中根结点的子树森林
  • 访问森林中第一棵树的根结点
  • 中序遍历除去第一棵树之后剩余的树构成的森林

森林先序==转化成的二叉树先序

森林后续==转化成的二叉树中序

在这里插入图片描述

5.5 树和二叉树的应用


5.5.1 哈夫曼树和哈夫曼编码

哈夫曼树的定义

:树结点被赋予某种意义的数值

结点的带权路径长度:从根结点到该结点的路径长度*该结点的权值

树的带权路径长度:所有叶子结点的带权路径长度之和WPL

哈夫曼树/最优二叉树:WPL最小的二叉树

哈夫曼树的构造

  1. 将这n个结点分别作为n棵仅含有一个结点的二叉树,构成森林F
  2. 构造一个新结点,从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且将新结点的权值置为左、右子树根结点的权值之和
  3. 将F中删除刚刚选出的两棵树,同时将新得到的树加入F中
  4. 重复上述的2,3步骤,直至F中只剩下一棵树为止

在这里插入图片描述

哈夫曼树的特点

  • 每个初始结点最终都成为叶结点,且权值越小的结点到根节点的路径长度越大
  • 构造过程中共新建了n-1个结点,因此哈夫曼树的结点总数为2n-1

哈夫曼树编码

固定长度编码:每个字符都用同样位数的二进制表示

可变长度编码:不同位数二进制表示

前缀编码:没有一个编码是另外一个编码的前缀,哈夫曼编码就是前缀码

构建过程

  1. 将每个字符作为一个独立的结点,权值为它出现的频度/次数,然后构建哈夫曼树
  2. 所有结点都出现在叶子结点上,用0标记左孩子,1标记右孩子
  3. 按照从根结点走向叶子结点的路径上的1,0来组成编码

在这里插入图片描述

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

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

相关文章

Linux学习笔记——HTTP协议

文章目录前言HTTP协议的概念URLurlencode和urldecodeHTTP协议格式HTTP协议版本HTTP协议请求方法GET方法——获取资源POST方法——获取资源PUT方法——传输⽂件PUT方法——获得报文首部在这里插入图片描述DELETE方法——删除文件OPTIONS方法——询问支持的方法HTTP的状态码HTTP常…

050:cesium加载mapbox卫星地图、mapbox地形地图

第050个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载mapbox卫星地图、mapbox地形地图。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共79行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设…

【CE】图形化作弊教程通关手册(超真实的游戏体验)

▒ 目录 ▒&#x1f6eb; 导读需求开发环境1️⃣ 第一关翻译操作总结2️⃣ 第二关&#xff08;代码共享&#xff09;翻译操作验证3️⃣ 第三关翻译操作总结&#x1f6ec; 文章小结&#x1f4d6; 参考资料&#x1f6eb; 导读 需求 除了Tutorial-x86_64.exe教程外&#xff0c;CE…

算法,离你并不遥远

算法&#xff0c;离你并不遥远引子什么是算法&#xff1f;为什么算法很重要&#xff1f;什么时候开始学算法&#xff1f;狭隘的算法定义算法从现在学起&#xff0c;从现在用起算法永不过时引子 今天又是搬砖摸鱼胡混的一天&#xff0c;在问答闲逛&#xff0c;看到了一个问答&a…

Linux多线程-2

“我是我境遇里的起步者。” -- 里尔克《布里格手记》 在上一节Linux多线程当中&#xff0c;我们讲述了Linux中线程的概念以及线程的控制内容。这一篇博客承接上次内容&#xff0c;我们来对线程安全进行阐述。 目录 1.线程安全 1.1互斥的实现 1.1.1互斥锁实现互斥 1.1.2互…

启动 Ethereum(上海) 主网全节点

问题描述 采用最新的geth版本之后&#xff0c;按照之前的方法启动geth主网节点会出现如下问题&#xff1a; Post-merge network, but no beacon client seen. Please launch one to follow the chain!问题原因 The above message is emitted when Geth is run without a conse…

PyTorch深度学习实战 | 神经网络的优化难题

即使我们可以利用反向传播来进行优化&#xff0c;但是训练过程中仍然会出现一系列的问题&#xff0c;比如鞍点、病态条件、梯度消失和梯度爆炸&#xff0c;对此我们首先提出了小批量随机梯度下降&#xff0c;并且基于批量随机梯度下降的不稳定的特点&#xff0c;继续对其做出方…

SpringBoot集成Dubbo启用gRPC协议

文章目录前言项目结构代码示例父工程api moduleservice module注意事项区别本文记录下SpringBoot集成Dubbo启用gRPC协议&#xff0c;以及与原生 gRPC 在代码编写过程中的区别。 下面还有投票&#xff0c;帮忙投个票&#x1f44d; 前言 Dubbo 在 2.7.5 版本开始支持原生 gRPC 协…

【软件测试】浅了解什么是软件测试及开发测试模型

目录 1.什么是软件测试&#xff1f; 2.什么是需求&#xff1f; 3.什么是测试用例 4.什么是软件错误&#xff08;bug&#xff09;&#xff1f; 5.开发模型和测试模型 5.1软件的生命周期 5.2瀑布模型 5.3螺旋模型 5.4软件测试V模型 5.5软件测试w模型 6.软件测试的生命周…

Redis第二十九讲 Redis集群发布订阅模式以及Redis集群事务

Redis集群状态下的发布订阅 在Redis的几个基本数据结构介绍中,有讲过List数据结构,可以使用List的阻塞特性实现订阅消费,关于Redis的底层数据结构可以参考我的这篇博客:Redis第六讲 Redis之List底层数据结构实现 底层数据结构基本操作可以看我的这篇博客,Redis第十五讲 R…

【从零到Offer】- 泛型

泛型是个啥 ​ Java 泛型是 JDK 5 中引入的一个新特性&#xff0c;其提供了编译时类型安全检测机制&#xff0c;该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型&#xff0c;也就是说所操作的数据类型被指定为一个参数。 ​ 通过将数据类型参数化&#xf…

用opencv+playwright过滑动验证码

目录 梳理思路 编写代码 总结与提高 在本节&#xff0c;我们将使用opencv和playwright这两个库通过QQ空间的滑动验证码。 梳理思路 1. 使用playwright打开浏览器&#xff0c;访问qq空间登录页面。 2. 点击密码登录。 3. 输入账号密码并点击登录。 4. 出现滑动验证码图片后…

大型语言模型综述,非常详细,格局打开!A Survey of Large Language Models

大型语言模型综述&#xff0c;非常详细&#xff0c;格局打开&#xff01;A Survey of Large Language Models 返回论文和资料目录 论文地址 项目地址 1.导读 讲得通俗易懂&#xff0c;且格局拉满&#xff01;基本覆盖了自ChatGPT以来的AI比较火的事件&#xff0c;还多次提到…

AI 模型首次有了国家标准!头部大厂参与编制,辐射 AMD

3月17日&#xff0c;国内首个面向人工智能生成式模型的国家标准正式公开&#xff0c;并向社会征求意见。 该标准全称为《信息技术神经网络表示与模型压缩第一部分&#xff1a;卷积神经网络》 &#xff08;GB&#xff0f;T 42382&#xff0e;1&#xff0d;2023&a…

【Linux系统下安装JDK】

一&#xff0c;linux下载JDK最方便快捷的方式&#xff1a;yum 1&#xff0c;执行下方命令&#xff0c;查看可安装java版本。 yum -y list java*2&#xff0c;选择一个进行安装&#xff0c;带-devel的安装的是jdk&#xff0c;而不带-devel的安装的是jre 3&#xff0c;安装命令…

【springBoot篇1】概念、创建和运行

目录 一、什么是springBoot&#xff1f;为什么要学springBoot springBoot的优点&#xff1a;(5点) 优点1&#xff1a;快速集成框架 优点2&#xff1a;内置了Tomcat容器 优点3&#xff1a;快速部署项目 优点4&#xff1a;少配置&#xff0c;多注解 优点5&#xff1a;支持更…

机器学习中的公平性

文章目录机器学习公平性评估指标群体公平性指标个人公平性指标引起机器学习模型不公平的潜在因素提升机器学习模型公平性的措施机器学习公平性 定义&#xff1a; 机器学习公平性主要研究如何通过解决或缓解“不公平”来增加模型的公平性&#xff0c;以及如何确保模型的输出结果…

生信刷题之ROSALIND——Part 1

目录写在前面1、Counting DNA NucleotidesProblemSample DatasetSample OutputCodeOutput2、Transcribing DNA into RNAProblemSample DatasetSample OutputCodeOutput3、Complementing a Strand of DNAProblemSample DatasetSample OutputCodeOutput4、Rabbits and Recurrence…

Android操作系统介绍

目录 Android 名词 Android LOGO 体系架构 Android系统架构 Linux 内核 硬件抽象层&#xff08;HAL&#xff09; Android Runtime 原生C/C库 Java API框架 系统应用 应用组件 活动 &#xff08;Activity) 服务 &#xff08;Service &#xff09; 广播接收器 &…

BLOOM模型结构详解

《BLOOM: A 176B-Parameter Open-Access Multilingual Language Model》 论文地址: https://arxiv.org/pdf/2211.05100.pdf 代码地址: transformers库-modeling_bloom.py BigScience 官方提供的代码链接并没有找到完整的模型实现代码,只有提示说模型结构代码是在 Megatron 的…