#数据结构 笔记三

二叉树

1. 概念

二叉树Binary Treen个结点的有限集合。它或者是空集n=0,或者是由一个根结点以及两颗互不相交、分别称为左子树和右子树的二叉树组成。

二叉树与普通有序树不同,二叉树严格区分左子和右子,即使只有一个子结点也要区分左右。

二叉树的树度数最大为2

2. 性质*

关于树的一些基本念

(1)度数:一个节点的子树的个数(一个节点的子树的个数称为该节点的度数,3)

(2)树度数:树中节点的最大度数

(3)叶节点或终端节点: 度数为零的节点

(4)分支节点:度数不为零的节点(B一层)

(5)内部节点:除根节点以外的分支节点 (B,C,D)

(6)节点层次: 根节点的层次为1,根节点子树的根为第2层,以此类推

(7)树的深度或高度: 树中所有节点层次的最大值 (4)

  1. 二叉树的第k层上的结点最多个2k-1
  2. 深度为k的二叉树最多有2k-1个结点

Sn=a1(1-qn)/(1-q)=a1(1-2k)/(1-2)=(1-2k)/-1=2k-1

  1. 在任意一颗二叉树中,树叶的数目比度数为2的结点数目多1

N:结点的总数

N0:没有子结点的结点个数

N1:只有一个子结点的结点个数

N2:有两个子结点的结点个数

总结点 = 各节点数目之和 N = N0 + N1 + N2

总结点 = 所有子节点 + 根 N = 0 × N0 + 1 × N1 + 2 × N2 + 1

联立以上两式可得: N0 = N2 + 1

(网易)一棵二叉树有8个度为2的节点,5个度为1的节点,那么度为0的节点个数为 ( 9 )

满二叉树和完全二叉树

满二叉树:深度为k(k>=1)时,第k层结点个数为2k-1

完全二叉树:只有最下面两层有度数小于2的节点,且最下面一层的结点集中在最左边的若干位置上。

3. 实现

二叉树的存储结构有两种,分为顺序存储和链式存储

3.1. 顺序存储

二叉树的顺序存储,指的是使用顺序表(数组)存储二叉树。需要注意,顺序存储只适用于完全二叉树。换句话说,只有完全二叉树才可以用顺序表存储。因此,如果我们想要顺序存储普通二叉树,就需要将其提前转换成完全二叉树。

普通二叉树转完全二叉树的方法很简单,只需给二叉树额外添加一些结点,将其"拼凑"成一个完全二叉树即可。

如图所示:

左侧是普通二叉树,右侧是转化后的完全(满)二叉树。

完全(满)二叉树的顺序存储,仅需要从根结点开始,按照层次依次将树中结点存储到数组即可。

存储图 2 所示的完全二叉树:

存储由普通二叉树转化来的完全二叉树也是如此。

图 1 中普通二叉树在顺组中的存储状态如图:

完全二叉树中结点按照层次并从左到右依次编号(123...),若结点i有左子,则其左子的结点编号为2*i,右子编号为2*i+1

设完全二叉树的结点数为n,某结点的编号为i

i>1时(不是根结点时),有父节点,其编号为i/2

2*i <= n时,有左子,其编号为2*i,否则没有左子,没左子一定没右子,其本身为叶节点。

2*i+1 <= n时,有右子,其编号为2*i+1,否则就没有右子。

3.1.1. 遍历*

先序:根----->左----->右

A B D H I E J C F K G

中序:左----->根----->右

H D I B E J A F K C G

后序:左----->右----->根

H I D J E B K F G C A

已知遍历结果如下,试画出对应的二叉树,写出后续:

先序:A B C E H F I J D G K 根----->左----->右

中序:A H E C I F J B D K G 左----->根----->右

因为先序是根在最前面的,所以在先序中从前往后地取结点拿到中序中作为根,循环重复。

3.2. 链式存储

链式存储此二叉树,从根结点开始,将各个结点以及其左右子的地址使用链表进行存储。

3.2.1. 定义操作完全二叉树结构体*

结点结构体由三部分组成:

  1. 指向左子结点的指针(Lchild)
  2. 结点存储的数据(结点编号)
  3. 指向右子结点的指针(Rchild)

// 1. 定义操作二叉树的结构体
typedef char datatype_tree;
typedef struct tree_node_t
{datatype_tree data;//数据域struct tree_node_t *lchild;//左子left struct tree_node_t *rchild;//右子right 
}bitree_node_t,*bitree_list_t;

3.2.2. 创建二叉树*

#include "bitree.h"// 2. 创建二叉树
// 主函数传参 n 树中结点总数; i 结点编号(从1开始)
bitree_list_t CreateBitree(int n,int i)
{// 2.1 开辟空间存放结构体bitree_list_t r = (bitree_list_t)malloc(sizeof(bitree_node_t));if(NULL == r){printf("CreateBitree malloc failed\n");return NULL;}// 2.2 初始化结构体成员// 2.3 判断有无左右子// 2.3.1 有左子r->data = i;if(2 * i <= n){r->lchild = CreateBitree(n,2*i);}// 2.3.2 无左子else{r->lchild = NULL;}// 2.3.3 有右子if(2*i + 1 <= n){r->rchild = CreateBitree(n,2*i+1);}// 2.3.4 无右子else{r->rchild = NULL;}return r;
}#include "bitree.h"
int main(int argc, const char *argv[])
{bitree_node_t *r = CreateBitree(3,1);return 0;
}

3.2.3. 先序遍历*

//前序
// 3. 先序遍历二叉树
// 根——左——右
void PreOrder(bitree_list_t r)
{if(NULL == r)return;printf("%d ",r->data);							 // 根// 如果有左子,则将左子作为根将该函数的全部操作走一遍if(r->lchild != NULL)							 // 左PreOrder(r->lchild);
// 如果有右子,则将右子作为根将该函数的全部操作走一遍if(r->rchild != NULL)PreOrder(r->rchild);						 // 右
}

3.2.4. 中序遍历

// 4. 中序遍历
// 左——根——右
void InOrder(bitree_list_t r)
{if(NULL == r)return;// 如果有左子,则将左子作为根将该函数的全部操作走一遍if(r->lchild != NULL)								 // 左InOrder(r->lchild);printf("%d ",r->data);								 // 根// 如果有右子,则将右子作为根将该函数的全部操作走一遍if(r->rchild != NULL)InOrder(r->rchild);									 // 右
}

3.2.5. 后序遍历

// 5. 后序遍历
// 左——右——根
void PostOrder(bitree_list_t r)
{if(NULL == r)return;// 如果有左子,则将左子作为根将该函数的全部操作走一遍if (r->lchild != NULL)                           // 左PostOrder(r->lchild);// 如果有右子,则将右子作为根将该函数的全部操作走一遍if (r->rchild != NULL)                           // 右PostOrder(r->rchild);printf("%d\t", r->data);                         // 根
}

总结:

#include "bitree.h"
bitree_list_t CreateBitree(int n,int i)
{bitree_list_t r = (bitree_list_t)malloc(sizeof(bitree_node_t));if(NULL == r){printf("CreateBitree malloc failed\n");return NULL;}r->data = i;if(2 * i <= n){r->lchild = CreateBitree(n,2*i);}else{r->lchild = NULL;}if(2*i + 1 <= n){r->rchild = CreateBitree(n,2*i+1);}else{r->rchild = NULL;}return r;
}
//前序
void PreOrder(bitree_list_t r)
{if(NULL == r)return;printf("%d ",r->data);if(r->lchild != NULL)PreOrder(r->lchild);if(r->rchild != NULL)PreOrder(r->rchild);
}
//中序
void InOrder(bitree_list_t r)
{if(NULL == r)return;if(r->lchild != NULL)InOrder(r->lchild);printf("%d ",r->data);if(r->rchild != NULL)InOrder(r->rchild);
}
//后序
void PostOrder(bitree_list_t r)
{if(NULL == r)return;if(r->lchild != NULL)PostOrder(r->lchild);if(r->rchild != NULL)PostOrder(r->rchild);printf("%d ",r->data);}
#include "bitree.h"
int main(int argc, const char *argv[])
{	bitree_node_t *r = CreateBitree(3,1);PreOrder(r);	printf("\n");InOrder(r);printf("\n");PostOrder(r);printf("\n");return 0;
}
#ifndef _BITREE_H_
#define _BITREE_H_
#include <stdio.h>
#include <stdlib.h>
typedef char datatype_tree;
typedef struct tree_node_t
{datatype_tree data;//数据域struct tree_node_t *lchild;//左子left struct tree_node_t *rchild;//右子right 
}bitree_node_t,*bitree_list_t;
bitree_list_t CreateBitree(int n,int i);
//前序
void PreOrder(bitree_list_t r);
//中序
void InOrder(bitree_list_t r);
//后序
void PostOrder(bitree_list_t r);
//层次
void unOrder(bitree_list_t r);
#endif	

3.2.6. 层序遍历

队列的思想

不需要敲代码,看懂就行

示意图

#ifndef _BITREE_H_
#define _BITREE_H_
typedef char datatype_tree;
typedef struct tree_node_t
{datatype_tree data;//数据域 struct tree_node_t *lchild;//左子指针struct tree_node_t *rchild;//右子指针
}bitree_t;
//前序遍历
void preOrder(bitree_t *r);//r二叉树根节点的指针
//中序遍历
void inOrder(bitree_t * r);
//后序遍历
void postOrder(bitree_t *r);
//遍历二叉树 
//s 代表的是打印提示, void (*p)(bitree_t *)函数指针  r遍历的树
void showBitree(char *s,void (*p)(bitree_t *),bitree_t *r);
//创建二叉树,用递归函数创建
bitree_t *createBitree();
//层次遍历
void unOrder(bitree_t *r);
#endif
#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_#include "bitree.h"//将 bitree_t * 改名 为datatype_linkqueue 
typedef bitree_t * datatype_linkqueue;//把队列的数据域变成指向树节点的指针
typedef struct node
{datatype_linkqueue data;//数据域struct node *next;//指针域
}linkqueue_node_t,*linkqueue_list_t;//linkqueue_list_t p === linkqueue_node_t *
typedef struct//将队列头指针和尾指针封装到一个结构体里
{linkqueue_list_t front;//相当于队列的头指针linkqueue_list_t rear;//相当于队列的尾指针//有了链表的头指针和尾指针,那么我们就可以操作这个链表
}linkqueue_t;//1.创建一个空的队列
linkqueue_t *createEmptyLinkQueue();
//2.入列 data代表入列的数据
int inLinkQueue(linkqueue_t *p,datatype_linkqueue data);
//3.出列 
datatype_linkqueue outLinkQueue(linkqueue_t *p);
//4.判断队列是否为空
int isEmptyLinkQueue(linkqueue_t *p);
//5.求队列长度的函数
int lengthLinkQueue(linkqueue_t *p);
//6.清空队列
void clearLinkQueue(linkqueue_t *p);
#endif
#include "bitree.h"
#include "linkqueue.h"
#include <stdio.h>
#include <stdlib.h>
//前序遍历
void preOrder(bitree_t *r)//r二叉树根节点的指针
{if(r == NULL)//递归函数的结束条件return;printf("%c ",r->data);//根preOrder(r->lchild);//左preOrder(r->rchild);//右
}
//中序遍历
void inOrder(bitree_t * r)
{if(r == NULL)//递归的结束条件return;inOrder(r->lchild);//左printf("%c ",r->data);//根inOrder(r->rchild);//右
}
//后序遍历
void postOrder(bitree_t *r)
{if(r == NULL)//递归函数的结束条件return;postOrder(r->lchild);//左postOrder(r->rchild);//右printf("%c ",r->data);//根
}
//遍历二叉树 
//s 代表的是打印提示, void (*p)(bitree_t *)函数指针  r遍历的树
void showBitree(char *s,void (*p)(bitree_t *),bitree_t *r)
{printf("%s",s);p(r);printf("\n");
}
//创建二叉树,用递归函数创建
bitree_t *createBitree()
{//root 
//	ABD###CE##F##bitree_t *r = NULL;//用来保存二叉树的根节点char ch;scanf("%c",&ch);if(ch == '#')//输入是'#',代表没有左子或右子return NULL;r = (bitree_t *)malloc(sizeof(bitree_t));if(NULL == r){perror("r malloc failed");return NULL;}r->data = ch;r->lchild = createBitree();r->rchild = createBitree();return r;
}//层次遍历
void unOrder(bitree_t *r)
{//1.创建一个队列,队列的数据域变成指向树节点的指针linkqueue_t *p = createEmptyLinkQueue();if(r != NULL)inLinkQueue(p,r);//2.循环打印while(!isEmptyLinkQueue(p)){r = outLinkQueue(p);printf("%c ",r->data);if(r->lchild != NULL)//只要左子不为空,就入列,之后出列的时候打印inLinkQueue(p,r->lchild);if(r->rchild != NULL)//只要右子不为空,就入列,之后出列的时候打印inLinkQueue(p,r->rchild);}
}
#include "linkqueue.h"
#include <stdio.h>
#include <stdlib.h>//1.创建一个空的队列
linkqueue_t *createEmptyLinkQueue()
{linkqueue_t *p = (linkqueue_t *)malloc(sizeof(linkqueue_t));if(NULL == p){perror("createEmptyLinkQueue p malloc failed");return NULL;}//申请空间就是为了装东西//申请链表的头节点空间,让rear和front都指向头结点p->front = p->rear = (linkqueue_list_t)malloc(sizeof(linkqueue_node_t));if(NULL == p->rear){perror("p->rear malloc failed");return NULL;}p->rear->next = NULL;//或者用p->front->next = NULL;因为p->rear 和 p->front 指向同一个位置即头节点return p;
}
//2.入列 data代表入列的数据
int inLinkQueue(linkqueue_t *p,datatype_linkqueue data)
{//1.创建一个新的节点,用来保存即将插入的数据linkqueue_list_t pnew = (linkqueue_list_t)malloc(sizeof(linkqueue_node_t));if(NULL == pnew){perror("inLinkQueue pnew malloc failed");return -1;}//2.将入列的数据放入到新的节点中pnew->data = data;pnew->next = NULL;//3.将新节点链链接到链表的尾巴p->rear->next = pnew;//新节点链接到链表的尾p->rear = pnew;//rear移动,因为rear永远指向当前链表的尾return 0;
}
//3.出列 
datatype_linkqueue outLinkQueue(linkqueue_t *p)
{linkqueue_list_t pdel = NULL;//指向被删除的节点//1.容错判断if(isEmptyLinkQueue(p)){printf("isEmptyLinkQueue !!\n");return NULL;}//2.出列数据//(1)定义pdel指向即将被删除的节点就是front指向的节点,出列每次删除的都是front指向的那个节点pdel = p->front;//(2)将front向后移动一个位置p->front = p->front->next;//(3)释放被删除节点free(pdel);pdel = NULL;//(4)将数据出列return p->front->data;
}
//4.判断队列是否为空
int isEmptyLinkQueue(linkqueue_t *p)
{return p->front == p->rear;
}
//5.求队列长度的函数
int lengthLinkQueue(linkqueue_t *p)
{int len = 0;linkqueue_list_t h = p->front;//将链表的头指针保存的地址给h,如果直接用front,求长度之后会找不到链表的头,用h的移动代替front的移动//求长度,相当于遍历有头的单向链表while(h->next != NULL){h = h->next;len++;}return len;
}
//6.清空队列
void clearLinkQueue(linkqueue_t *p)
{while(!isEmptyLinkQueue(p))//只要不为空,就出列outLinkQueue(p);
}
#include "bitree.h"
#include <stdio.h>
#include <stdlib.h>int main(int argc, const char *argv[])
{bitree_t *r = createBitree();showBitree("前序:",preOrder,r);showBitree("中序:",inOrder,r);showBitree("后序:",postOrder,r);showBitree("层次:",unOrder,r);return 0;
}

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

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

相关文章

React@16.x(47)路由v5.x(12)源码(4)- 实现 Route

目录 1&#xff0c;原生 Route 的渲染内容2&#xff0c;实现 1&#xff0c;原生 Route 的渲染内容 对如下代码来说&#xff1a; import { BrowserRouter as Router, Route } from "react-router-dom"; function News() {return <div>News</div>; }func…

【虚拟机】虚拟机网络无法访问问题【已解决】

【虚拟机】虚拟机无法上网问题【已解决】 问题探究解决方法法1&#xff1a;查看相关“网络服务”是否处于正常启动状态法2&#xff1a;重启网络法3&#xff1a;重新安装VMWare法4&#xff1a;使用NAT模式&#xff0c;每次打开win7都没连上网的解决办法 问题探究 安装了很多个虚…

上位机图像处理和嵌入式模块部署(mcu项目1:实现协议)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 这种mcu的嵌入式模块理论上都是私有协议&#xff0c;因为上位机和下位机都是自己开发的&#xff0c;所以只需要自己保证上、下位机可以通讯上&…

Feign 原理流程图练习-01

目录 作业: 老师给的参考流程图 要求 解答 知识扩展 Feign基础原理 接口定义 代理对象生成 请求调用 请求发送 响应处理 容错与熔断 总结 作业: 老师给的参考流程图 pdf版本 【金山文档 | WPS云文档】 Feign https://kdocs.cn/l/ctbagIyxN348 ​ 要求 结合上面…

基于Redisson实现分布式锁

基于redisson实现分布式锁 之前背过分布式锁几种实现方案的八股文&#xff0c;但是并没有真正自己实操过。现在对AOP有了更深一点的理解&#xff0c;就自己来实现一遍。 1、分布式锁的基础知识 分布式锁是相对于普通的锁的。普通的锁在具体的方法层面去锁&#xff0c;单体应…

班迪录屏(Bandicam)7.0下载以及安装教程

最近有小伙伴私信我&#xff0c;问我有没有好用的录屏工具&#xff0c;今天给大家分享一个我一直在使用的录屏工具&#xff0c;也是解锁了V1P版本&#xff0c;绿色版打开就可以使用~ Bandicam录屏&#xff08;PC&#xff09; Bandicam录屏是一款专为捕捉屏幕精彩瞬间而设计的…

PHP家政服务预约单开版微信小程序系统源码

&#x1f3e0; —— 便捷生活&#xff0c;从指尖开始&#x1f4aa; &#x1f308;【开篇&#xff1a;家政新风尚&#xff0c;一键触达】 在忙碌的生活节奏中&#xff0c;你是否渴望拥有一个温馨、整洁的家&#xff0c;却又苦于找不到合适的家政服务&#xff1f;现在&#xff…

EXTI寄存器,AFIO的简洁,EXTI配置的流程

一&#xff0c;AFIO简介 AFIO是Alternate Function Input/Output 的缩写&#xff0c;表示复用功能IO&#xff0c;主要用于实现IO端口的复用功能以及外部中断的控制 STM32外设有很多I/O以及内置外设&#xff08;如12C&#xff0c;ADC,ISP,USART等&#xff09;。为节省引出管脚的…

初试成绩占比百分之70!计算机专硕均分340+!华中师范大学计算机考研考情分析!

华中师范大学&#xff08;Central China Normal University&#xff09;简称“华中师大”或“华大”&#xff0c;位于湖北省会武汉&#xff0c;是中华人民共和国教育部直属重点综合性师范大学&#xff0c;国家“211工程”、“985工程优势学科创新平台”重点建设院校&#xff0c…

PhysioLLM 个性化健康洞察:手表可穿戴设备实时数据 + 大模型

个性化健康洞察&#xff1a;可穿戴设备实时数据 大模型 提出背景PhysioLLM 图PhysioLLM 实现数据准备用户模型和洞察生成个性化数据总结和洞察是如何生成的&#xff1f; 解析分析 提出背景 论文&#xff1a;https://arxiv.org/pdf/2406.19283 虽然当前的可穿戴设备伴随应用&…

最近,被“AI”狠狠刷屏了......

最近&#xff0c;被“AI”狠狠刷屏了。 作为时下最热门的话题&#xff0c;AI画图、AI配音、AI写文案、AI做视频......AI在最近两年可谓是火遍全球。ChatGPT、Midjourney和SORA等技术不断涌现&#xff0c;不仅深刻改变着我们的生活方式&#xff0c;也推动了AI技术的飞速发展。 …

vue使用scrollreveal和animejs实现页面滑动到指定位置后再开始执行动画效果

效果图 效果链接&#xff1a;http://website.livequeen.top 介绍 一、Scrollreveal ScrollReveal 是一个 JavaScript 库&#xff0c;用于在元素进入/离开视口时轻松实现动画效果。 ScrollReveal 官网链接&#xff1a;ScrollReveal 二、animejs animejs是一个好用的动画库…

nvm安装以及idea下vue启动项目过程和注意事项

注意1&#xff1a;nvm版本不要太低&#xff0c;1.1.7会出现下面这个问题&#xff0c;建议1.1.10及其以上版本 然后安装这个教程安装nvm和node.js 链接: nvm安装教程&#xff08;一篇文章所有问题全搞定&#xff0c;非常详细&#xff09; 注意2&#xff1a;上面的教程有一步骤…

实现各平台确定性的物理碰撞

1.使用FixedUpdate而不是Update 1.物理运算&#xff0c;比如刚体运动系统的运算是通过固定的时间来驱动的。 2.再moba帧同步游戏中&#xff0c;15帧的固定调用差不多是网络那边的极限了&#xff0c;采用其他手段如平滑显示来提高画面的平滑度。 FixedUpdate是以一个固定的帧率…

开始尝试从0写一个项目--前端(一)

基础项目构建 创建VUE初始工程 确保自己下载了node.js和npm node -v //查看node.js的版本 npm -v //查看npm的版本 npm i vue/cli -g //安装VUE CLI 创建 以管理员身份运行 输入&#xff1a;vue ui 就会进入 点击创建 自定义项目名字&#xff0c;选择npm管理 结…

“工控机”是什么?和普通电脑有区别嘛!

在现代工业生产中,有一种特殊的计算机,它不像普通电脑那样被放置于明亮的办公室内,而是常常藏身于机器轰鸣、环境恶劣的工厂车间里,这就是工控机——工业控制计算机的简称。作为工业自动化领域不可或缺的核心设备,工控机不仅承载着监控与数据采集(SCADA)、过程控制、数据…

概率论与数理统计_下_科学出版社

contents 前言第5章 大数定律与中心极限定理独立同分布中心极限定理 第6章 数理统计的基本概念6.1 总体与样本6.2 经验分布与频率直方图6.3 统计量6.4 正态总体抽样分布定理6.4.1 卡方分布、t 分布、F 分布6.4.2 正态总体抽样分布基本定理 第7章 参数估计7.1 点估计7.1.1 矩估计…

上海-灵曼科技(面经)

上海-灵曼科技 hr电话面 个人简介 个人信息的询问 是否知道芋道框架 技术面 算法题 14. 最长公共前缀&#xff08;写出来即可&#xff09; 聊一下Docker Docker核心概念总结Docker实战 聊一下AOP Spring AOP详解 聊一下JWT JWT 基础概念详解JWT 身份认证优缺点分析 Spri…

使用React复刻ThreeJS官网示例——keyframes动画

最近在看three.js相关的东西&#xff0c;想着学习一下threejs给的examples。源码是用html结合js写的&#xff0c;恰好最近也在学习react&#xff0c;就用react框架学习一下。 本文参考的是threeJs给的第一个示例 three.js examples (threejs.org) 一、下载threeJS源码 通常我们…

【接口自动化测试】第四节.实现项目核心业务的单接口自动化测试

文章目录 前言一、登录单接口自动化测试 1.1 登录单接口文档信息 1.2 登录成功 1.3 登录失败&#xff08;用户名为空&#xff09;二、数据驱动的实现 2.1 json文件实现数据驱动三、课程添加单接口自动化测试 3.1 课程添加单接口文档信息 3.2 课程…