二叉树 初阶 总结

树的基础认知

        

结点的度:一个结点含有的子树的个数称为该结点的度; 如上图:A的为6
叶结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I...等结点为叶结点
非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G...等结点为分支结点
双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点
孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点
兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点
树的度:一棵树中,最大的结点的度称为树的度; 如上图:树的度为6
结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推;
树的高度或深度:树中结点的最大层次; 如上图:树的高度为4
堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点
结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先
子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙
森林:由m(m>0)棵互不相交的树的集合称为森林;

树的表示

typedef int DataType;
struct Node
{
struct Node* left; // 左节点
struct Node* right; // 右节点
DataType val; // 结点中的数据域
};

满二叉树和完全二叉树

        这里很好辨析,满二叉树就是满的完全二叉树

         

树的性质

         1. 若规定根结点的层数为1,则一棵非空二叉树的第2^(i-1)层上最多有 个结点.
         2. 若规定根结点的层数为1,则深度为h的二叉树的最大结点数是2^h - 1.
         3. 对任何一棵二叉树, 如果度为0其叶结点个数为n, 度为2的分支结点个数m为 ,则有m=n+1           4. 若规定根结点的层数为1,具有n个结点的满二叉树的深度,h=log(n+1).
         5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从0开                始编号,则对于序号为i的结点有:
              1. 若i>0,i位置结点的双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
              2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
              3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子
 

树的两种存储结构

        顺序存储

        顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

         链式存储

        二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
struct BinTreeNode* left; // 指向当前结点左孩子
struct BinTreeNode* right; // 指向当前结点右孩子
BTDataType data; // 当前结点值域
}
// 三叉链
struct BinaryTreeNode
{
struct BinTreeNode* parent; // 指向当前结点的双亲
struct BinTreeNode* left; // 指向当前结点左孩子
struct BinTreeNode* right; // 指向当前结点右孩子
BTDataType data; // 当前结点值域
};

 堆

        为了能更好的认识二叉树,我们需要认识一下堆。

        普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段

        堆分小根堆,和大根堆。小根堆是根比孩子小,大根堆是跟比孩子大。

                             小根堆                                                               大根堆

        代码实现

typedef int HPDataType;
typedef struct Heap
{
HPDataType* _a;
int _size;
int _capacity;
}Heap;
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);

 向下调整算法

        现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根结点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int true[] = {1,3,5,7,9,2,4,6,8,10};
void HeapDown(HPDataType* a, int size,int parent)
{int child = parent * 2 + 1;//通过父母找孩子 while (child < size)//孩子小于整个堆的大小{if (child +1 < size && a[child] > a[child + 1])//右孩子小于整个堆且左孩子大{child++;//调整到右孩子来看 这里调整到更小或者更大的孩子那一边进行调整}if (a[parent] > a[child])//如果父母比孩子大或小就交换位置{Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

 向上调整算法

void HeapUp(HPDataType * a,int child)
{int parent = (child - 1) / 2;//通过孩子找父母while (child != 0){if (a[parent] > a[child])//根据大小关系交换位置{Swap(&a[parent], &a[child]);child = (child - 1) / 2;parent = (child - 1) / 2;}else{break;}}
}

其它部分: 

//堆的初始化
void HeapInit(Heap* php)
{assert(php);php->_a = NULL;php->_capacity = php->_size = 0;
}
// 堆的销毁
void HeapDestory(Heap* hp)
{assert(hp);free(hp->_a);hp->_a = NULL;hp->_capacity = hp->_size = 0;
}
// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{assert(hp);//扩容if (hp->_capacity == hp->_size){int newcapacity = hp->_capacity == 0 ? 4 : 2 * hp->_capacity;Heap* newa = (Heap*)realloc(hp->_a, newcapacity * sizeof(HPDataType));if (newa == NULL){perror("error realloc");exit(1);}hp->_a = newa;hp->_capacity = newcapacity;}//插入hp->_a[hp->_size] = x;hp->_size++;//向上变换函数HeapUp(hp->_a, hp->_size-1);
}
// 堆的删除
void HeapPop(Heap* hp)
{assert(hp && hp->_size > 0);Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);hp->_size--;HeapDown(hp->_a, hp->_size, 0);
}
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{assert(hp && hp->_size);return hp->_a[0];
}
// 堆的数据个数
int HeapSize(Heap* hp)
{assert(hp);return hp->_size;
}
// 堆的判空
int HeapEmpty(Heap* hp)
{assert(hp);return hp->_size == 0;
}

 二叉树

        手搓二叉树

typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->_left = node2;
node1->_right = node4;
node2->_left = node3;
node4->_left = node5;
node4->_right = node6;
return node1;
}

二叉树的三种遍历

前序遍历 中序遍历 后序遍历 也就是 根左右 左根右 右根左 

根左右就是先根再左再右的遍历 以此类推

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL)return;printf("%c", root->_data);BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{if (root == NULL)return;	BinaryTreePrevOrder(root->_left);printf("%c", root->_data);BinaryTreePrevOrder(root->_right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{if (root == NULL)return;BinaryTreePrevOrder(root->_left);BinaryTreePrevOrder(root->_right);printf("%c", root->_data);
}

 他们的区别就是在什么时候”输出“

依照遍历创建二叉树

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a,int* pi)
{if (a[*pi] == '#'){(*pi)++;return NULL;}BTNode* root = (BTNode*)malloc(sizeof(BTNode));root->_data = a[*pi];(*pi)++;root->_left = BinaryTreeCreate(a, pi);root->_right = BinaryTreeCreate(a, pi);return root;
}

层序遍历

         层序遍历需要用到队列

        队列代码:

typedef struct BinaryTreeNode* QDataType;typedef struct QueueNode
{struct QueueNode * next;QDataType val;
}QNode;typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;void QueueInit(Queue* p)
{assert(p);p->phead = p->ptail = NULL;p->size = 0;
}
void QueuePush(Queue* p, QDataType x)//尾插
{assert(p);QNode* next = (QNode*)malloc(sizeof(QNode));if (next == NULL){perror("error malloc");exit(1);}next->next = NULL;next->val = x;if (p->phead != NULL){p->ptail->next = next;p->ptail = p->ptail->next;}else{p->phead = p->ptail = next;}p->size++;
}
//头删
void QueuePop(Queue* p)
{assert(p&&p->phead);if (p->phead->next == NULL){free(p->phead);p->phead = p->ptail = NULL;}else{QNode* ptr = p->phead->next;free(p->phead);p->phead = ptr;}p->size--;
}
//读取队列的长度
QDataType QueueSize(Queue* p)
{assert(p);return p->size;
}
//摧毁队列
void QueueDestroy(Queue* p)
{assert(p);while (p->phead){QueuePop(p);}p->phead = p->ptail = NULL;p->size = 0;
}bool QueueEmpty(Queue* p)
{assert(p);return p->size == 0;
}
//查看头部元素
QDataType QueueFront(Queue* p)
{assert(p && p->ptail);return p->phead->val;
}
//查看尾部元素
QDataType QueueBack(Queue* p)
{assert(p && p->ptail);return p->ptail->val;
}

 层序遍历代码

void BinaryTreeLevelOrder(BTNode* root)//广度优先遍历
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%c", front->_data);if (front->_left)QueuePush(&q, front->_left);if (front->_right)QueuePush(&q, front->_right);}QueueDestroy(&q);
}

这里的思路是利用队列 进一个出一个进行 也就是从左到右 从上到下依次入队列 出队列的检查

判断二叉树是否是完全二叉树

// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}QueuePush(&q, front->_left);QueuePush(&q, front->_right);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front){QueueDestroy(&q);return false;}}QueueDestroy(&q);return true;
}

 在层序遍历的基础上,如果找到了第一个NULL,那么就要保证之后的元素都是NULL

原因是因为 完全二叉树的结构是在不完整的上一层必然完整 而且 不完整的下一层不能有新的元素存在 层序且统计 首个NULL存在的层数 而且也统计了 该层的下一层的元素 如果该NULL之后的元素有非NULL 就不是完全二叉树。

其它:

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)//这个用后续遍历进行销毁操作
{if (*root == NULL)return;BinaryTreeDestory(&(*root)->_left);BinaryTreeDestory(&(*root)->_right);free(*root);*root = NULL;
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL)return 0;	return 1 + BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right);	
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->_left == root->_right)return 1;return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL)return 0;if (k == 1)return 1;return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->_data == x)return root;BTNode* left = BinaryTreeFind(root->_left, x);BTNode* right = BinaryTreeFind(root->_right, x);if (left && left->_data == x)return left;if (right && right->_data == x)return right;
}

 

 

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

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

相关文章

采用T网络反馈电路的运算放大器(运放)反相放大器

运算放大器(运放)反相放大器电路 设计目标 输入电压ViMin输入电压ViMax输出电压VoMin输出电压VoMaxBW fp电源电压Vcc电源电压Vee-2.5mV2.5mV–2.5V2.5V5kHz5V–5V 设计说明1 该设计将输入信号 Vin 反相并应用 1000V/V 或 60dB 的信号增益。具有 T 反馈网络的反相放大器可用…

【鸿蒙学习笔记】位置设置・position・绝对定位・子组件相对父组件

官方文档&#xff1a;位置设置 目录标题 position・绝对定位・子组件相对父组件Row Text position position・绝对定位・子组件相对父组件 正→ ↓ Row Text position Entry Component struct Loc_position {State message: string Hello World;build() {Column() {Co…

【Neural signal processing and analysis zero to hero】- 1

The basics of neural signal processing course from youtube: 传送地址 Possible preprocessing steps Signal artifacts (not) to worry about doing visual based artifact rejection so that means that before you start analyzing, you can identify those data epic…

Elasticsearch:如何选择向量数据库?

作者&#xff1a;来自 Elastic Elastic Platform Team 向量数据库领域是一个快速发展的领域&#xff0c;它正在改变我们管理和搜索数据的方式。与传统数据库不同&#xff0c;向量数据库以向量的形式存储和管理数据。这种独特的方法可以实现更精确、更相关的搜索&#xff0c;并允…

【HarmonyOS】关于鸿蒙消息推送的心得体会 (一)

【HarmonyOS】关于鸿蒙消息推送的心得体会&#xff08;一&#xff09; 前言 这几天调研了鸿蒙消息推送的实现方式&#xff0c;形成了开发设计方案&#xff0c;颇有体会&#xff0c;与各位分享。 虽然没做之前觉得很简单的小功能&#xff0c;貌似只需要和华为服务器通信&…

Unity XR Interaction Toolkit的安装(二)

提示&#xff1a;文章有错误的地方&#xff0c;还望诸位大神不吝指教&#xff01; 文章目录 前言一、安装1.打开unity项目2.打开包管理器&#xff08;PackageManage&#xff09;3.导入Input System依赖包4.Interaction Layers unity设置总结 前言 安装前请注意&#xff1a;需要…

科技论文在线--适合练习期刊写作和快速发表科技成果论文投稿网站

中国科技论文在线这个平台可以作为练手的一个渠道&#xff0c;至少可以锻炼一下中文写作&#xff0c;或者写一些科研方向的简单综述性文章。当然&#xff0c;如果你的老师期末要求也是交一份科技论文在线的刊载证明的话&#xff0c;这篇文章可以给你提供一些经验。 中国科技论…

【Linux服务器Java环境搭建】011在linux中安装Nginx,以及停止或启动Nginx服务

系列文章目录 【Linux服务器Java环境搭建】 前言 又到了周五晚上了&#xff0c;最近工作上有些忙&#xff0c;忙于一个需求频繁变更的项目&#xff0c;都快吐血了&#xff0c;懂得都懂&#xff0c;哈哈&#xff0c;正好有时间了&#xff0c;继续写系列【Linux服务器Java环境搭…

linux远程主机和windows互传

一.winscp 最简单&#xff0c;但有时候会出现连不上 二 .MobaXterm 可以选择多种连接方式 二. 配置samba服务器 1. 新增samba用户 sudo pdbedit -L -v 查看当前samba用户 sudo smbpasswd -a guoziyi 添加samba用户 sudo smbpasswd -x guoziyi 删除samba用户 2. 编辑/etc…

加油卡APP系统开发:在线优惠加油,拓展市场

目前&#xff0c;我国汽车行业发展迅速&#xff0c;用车群体逐年扩大&#xff0c;因此&#xff0c;汽车加油市场规模呈现出了快速增长趋势。不过近年来&#xff0c;油价不断上涨&#xff0c;增加了居民的生活成本&#xff0c;为了节省汽车加油的支出&#xff0c;很多人都开始选…

使用idea创建Javaweb项目(步骤)

第一步创建Javaweb项目 File>New>Project 第二步 勾选Web Application >Next 然后就是进行起名&#xff0c;完成。 完成创建项目&#xff0c;检查是否文件齐全 配置tomcat 配置好&#xff0c;就能启动tomcat&#xff0c;显示首页 导入jar包。导入进项目&#xf…

【力扣】最小栈

&#x1f525;博客主页&#xff1a; 我要成为C领域大神&#x1f3a5;系列专栏&#xff1a;【C核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 设计一个支持 push…

信息系统是一个社会技术系统

一.管理信息系统定义 (1) 技术视角 信息系统是&#xff1a; ——由若干相互连接的部件组成的&#xff1b; ——对组织中的信息进行收集、处理、储存和传递的系统&#xff1b; ——用以支持组织制定决策和管理控制&#xff1b; ——还可以协助管理者与员工分析问题、可视…

【异常解决】Unable to start embedded Tomcat Nacos 启动报错

Unable to start embedded Tomcat Nacos 启动报错解决方案 一、背景描述二、原因分析三、解决方案 一、背景描述 Windows 本地启动 Nacos&#xff08;2.2.0&#xff09; 服务&#xff0c;控制台报错 Unable to start embedded Tomcat。 报错信息&#xff1a;Unable to start …

nginx负载均衡实例

实现效果 浏览器输入地址http://nginx服务器ip(:80)/edu/a.html&#xff0c;实现负债均衡效果&#xff0c;平均分配到 服务器ip:8080和 服务器ip:8081进程中。 准备工作 准备两个tomcat&#xff0c;一个监听在8080端口&#xff0c;一个监听在8081端口。也可以准备多个tomcat。…

数据结构小测试:排序算法

目录 1、请简述数据结构八大排序算法的思路。 2、常用排序算法手写 冒泡排序&#xff1a; 选择排序&#xff1a; 快速排序&#xff1a; 归并排序&#xff1a; 堆排序&#xff1a; 3、额外再加一个二分查找吧 1、请简述数据结构八大排序算法的思路。 冒泡排序&#xff…

Linux——多路复用之select

目录 前言 一、select的认识 二、select的接口 三、select的使用 四、select的优缺点 前言 在前面&#xff0c;我们学习了五种IO模型&#xff0c;对IO有了基本的认识&#xff0c;知道了select效率很高&#xff0c;可以等待多个文件描述符&#xff0c;那他是如何等待的呢&a…

易保全参与起草的两项区块链全国团体标准正式发布

在数字化转型浪潮席卷全球的今天&#xff0c;区块链技术以其去中心化、透明性、不可篡改等独特优势&#xff0c;正逐步成为重塑各行各业信任机制与业务流程的关键力量。 近日&#xff0c;中国通信工业协会正式发布了《区块链服务 基于区块链的去中心化标识符技术要求》与《区块…

python--实验14 并发编程(多线程)

知识点 1 并发编程 1.1程序提速手段 1.2多任务 并发 在一个CPU一段时间内交替去执行任务。在各个任务之间快速地切换&#xff0c;给人的感觉就是多个任务在“同时进行”。 并行 对于多核CPU处理多任务&#xff0c;操作系统会给CPU的每个内核安排一个执行的软件&#xff0c;多…

[论文笔记] CT数据配比方法论——1、Motivation

我正在写这方面的论文,感兴趣的可以和我一起讨论!!!!!! Motivation 1、探测原有模型的配比: 配比 与 ppl, loss, bpw, benchmark等指标 之间的关系。 2、效果稳定的配比:配比 与 模型效果 之间的规律。 Experiments 1、主语言(什么语言作为主语言,几种主语言?…