数据结构(C):从初识堆到堆排序的实现

server/2024/12/23 7:33:51/

🌞0.前言

        言C之言,聊C之识,以C会友,共向远方。各位博友的各位你们好啊,这里是持续分享数据结构知识的小赵同学,今天要分享的数据结构知识是,在这一章,小赵将会向大家展开聊聊的相关知识。✊

🚈 1.的概念

就是以 二叉树的顺序存储方式来存储元素,同时又要满足 父亲结点存储数据都要大于等于儿子结点存储数据(也可以是父亲结点数据都要小于等于儿子结点数据)的一种数据结构只有两种即大和小,大就是父亲结点数据大于等于儿子结点数据,小则反之。

同时这里要注意的是一定是完全二叉树,不然就不是。那完全二叉树是什么呢?这个地方不懂的博友可以看我们的这一篇博客:数据结构(C)树的概念和二叉树初见http://t.csdnimg.cn/JnWfb

如果看完了还是不明白可以私信小赵询问哦。

好了下面让我们看看上面说的两种在图上是怎么呈现的呢?

 

我们发现我们的任意一个父节点都比他的两个子节点大(或等于)这个时候这就是一个大。 

我们发现我们的任意一个父节点都比他的两个子节点小(或等于)这个时候这就是一个小。 

虽然是完全二叉树,但其的存储方式却与二叉树不同,我们一般存储的方式是数组。而我们上面所画的叫逻辑结构,那怎么由数组的存储方式,转化为我们的逻辑结构呢?

我们在介绍二叉树的时候其实也曾简单的说过二叉树是有顺序的,是从上到下,从左向右的,按这个顺序我们就可以给我们的二叉树标序号。 

那么就可以我们的逻辑结构转化成我们的数组结构了,既然我们已经会将逻辑结构转化为数组结构了,那么将数组结构转化为逻辑结构也就没有这么难了,大家可以自己试试,如果实在实现不了也可以找小赵咨询哦。 

🚈 2.的实现

🚝2.1向下调整算法

什么是向下调整算法呢?其实正如其名就是从上到下调整的意思。要想更深入了解这个东西就先来看这张图。

看这张图这是一个很明显的小对吧,那我这个时候要你改成一个大怎么办,这个时候的操作其实是2,3进行比较,然后拿出大的和1换这个就成大了。 

可如果这个时候我给你的是个这样的你又该怎么办(要改成小

 其实这个时候也是可以操作的,因为下面是有序的,我们只需要按照前面的顺序一步步来就可以完成了。只不过这个时候改成了选择两个子中的小的哪一个,因为我们要做得是小(这个时候之所以说他下面是有序的是因为盖住最上面的一个,下面的两个都是小,我们要改成的也是小。)

那如果是无序的呢? (改成小)

这个时候我们发现我们再想把这个改成小的难度就很大了。

🚝2.2的创建(向下调整算法

那通过上面的实验我们发现,想通过一个位置来做上面的操作,并把整个都变成小(大),必须下面就是一个小(大)。所以我们的向下调整其实也是从最下面的开始的,而且我们刚刚做的步骤其实就是向下调整。 

那么再面对上面那个无序的我们也就有方法了。

这个时候我们就可以做到将无序的转化成有序的了。 那么这个时候我们面对任何一个无序的数组,都可以通过这样的方式将他转化成.(至少在逻辑图上可以实现,代码实现下面说)

✈️2.2.1 向下调整建时间复杂度

每层节点个数 × 最坏情况向下调整次数:
T(N) = 2^(h-2) × 1 + 2^(h-3) × 2 + … … + 2^1 × (h-2)+2^0*(h-1)

错位相减法
等号左右两边乘个 2 得到一个新公式,再用新公式减去旧的公式,具体见下图

🚝2.3向上调整算法

聊了向下调整算法,那么其实也有向上调整算法

如这里我们如何把这个改成大

向上调整一般是怎么玩的呢,一般我们的向上调整法就是从下面往上调整,向这里要想调整到我们想要的样子就要三步

🚝2.4的创建(向上调整算法

这里呢也和上面一样,向上调整的地方的上面必须是已经调整好的,所以我们一般用向上调整法去建的时候往往要从第一个开始建到最下面。

✈️2.4.1向上调整算法的时间复杂度

T(N) = 2^1× 1 + 2^2× 2 + 2^3 ×3+ … … + 2^(h-3)× (h-3) + 2^(h-2) × (h-2)+ 2^(h-1) × (h-1)

综上:向下调整建要更优秀,效率更高

但总的来建的时间复杂度是O(N*logN)

🚝2.5的插入

像我们一般是用数组存储的,我们一般插入也是从数组后面插入,所以也就是在最后一个位置插入,而这个时候上面的都是有顺序的,我们可以用我们的向上调整算法来解决的插入。

🚝2.6的删除

的删除,我们一般针对的是头元素的删除,这个时候我们采取的策略是将头元素和尾元素交换,然后让头元素,向下调整(因为下面的都已经是有顺序的)

这样就可以完成头删了 

🚝2.7建的代码实现

代码的实现我们的表示表示方式和前面的双亲节点是对应的(parent),然后子节点就是(child)

✈️2.7.1向下调整实现的建立

//小的建立
void Swap(int* a, int* b)//交换
{int tmp = *a;*a = *b;*b = tmp;
}
//小
void ADjustDown(int *a,int parent,int n)
{int child = parent * 2 + 1;//子节点和双亲节点的关系while (child < n){if (child + 1 < n && a[child + 1] < a[child])//选出两个孩子中较小的一个{child++;}if (a[child] < a[parent])//若子节点比双亲节点小{Swap(&a[child], &a[parent]);//交换接着向下进行parent = child;child = parent * 2 + 1;}else{break;}}
}
void Heap(int *a,int n)
{int i = (n - 1 - 1) / 2;//找到最后一个双亲节点while (i >= 0){ADjustDown(a, i, n);i--;}
}
int main()
{int a[10] = { 3,5,9,8,6,1,5,2,6,323 };Heap(a, 10);
}

这里的主要方式就是我们前面的图所演示的,就是从最后一个双亲节点向下调整,然后调整过程中要小心不要让字节出了数组的范围,同时要记住子节点和双亲节点的关系。 这样可以方便我们的代码的实现。

✈️2.7.2向上调整实现的建立

//建立小
void Swap(int* a, int* b)//交换
{int tmp = *a;*a = *b;*b = tmp;
}
void ADjustUp(int*a,int child,int n)
{int parent = (child - 1) / 2;//双亲节点和子节点关系while (parent >= 0){if (a[child] < a[parent])//如果子节点比双亲节点小,那么交换,并且向上移动{Swap(&a[child], &a[parent]);child = parent;parent = (child - 1 - 1) / 2;}else{break;}}
}
void Heap(int* a, int n)
{int i = 0;while (i < n)//从最上面开始建{ADjustUp(a, i, n);i++;}
}
int main()
{int a[10] = { 3,5,9,87,6,12,7,11,13,1 };Heap(a, 10);
}

这里也基本上就是我们上面逻辑的实现,向上建,就要保证上面都是已经建好的,那么就得从首元素开始依次向下,建。 

🚈 3.完整的的代码的实现

要想实现完整的的实现就要实现以下几个函数

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);

这样我们的就可以像之前的链表顺序表等一样使用了。 

🚝3.1的创建

和上面的代码逻辑基本一样。

这里我们主要采用的是向下排序建,因为向下排序的时间复杂度低。

void HeapCreate(Heap* hp, HPDataType* a, int n)
{hp->_a = (HPDataType*)malloc(sizeof(HPDataType) * n);if (hp->_a == NULL){perror("malloc failed");}memcpy(hp->_a, a, n);hp->_capacity = n;hp->_size = n;int i = (n - 1 - 1) / 2;while (i >= 0){ADjustDown(a, i, n);i--;}
}

🚝3.2的销毁

void HeapDestory(Heap* hp)
{free(hp->_a);hp->_a = NULL;hp->_capacity = 0;hp->_size = 0;
}

🚝3.3的插入

void HeapPush(Heap* hp, HPDataType x)
{if (hp->_size == hp->_capacity){int newcapacity = (hp->_size == 0 ? 4 : hp->_capacity * 2);//如果没有内存就给4,有就是原来内存的双倍HPDataType* tmp = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity);if (tmp == NULL){perror("realloc failed");exit(-1);//退出程序,返回-1;}hp->_a = tmp;hp->_capacity = newcapacity;}hp->_a[hp->_size] = x;hp->_size++;ADjustUp(hp->_a, hp->_size - 1, hp->_size);//上面都是建好的,只需要向上调整即可
}

🚝3.4的删除(头删)

void HeapPop(Heap* hp)
{if (!HeapEmpty(hp)){Swap(&(hp->_a[0]), &(hp->_a[hp->_size - 1]));//首尾交换ADjustDown(hp->_a, 0, hp->_size);//向下调整hp->_size--;//删除尾}else{return;}
}

🚝3.5取顶元素的数据

HPDataType HeapTop(Heap* hp)
{if (!HeapEmpty(hp))//不是空集,返回首元素(即顶元素){return hp->_a[0];}else{return NULL;}
}

🚝3.6 的数据个数

int HeapSize(Heap* hp)
{return hp->_size;
}

🚝3.7的判空

int HeapEmpty(Heap* hp)
{return hp->_size == 0;//如果等式成立返回非零数,不成立返回零
}

🚈 4.的应用排序

下面我们进入排序的学习,排序是我们的排序中比较优的一个排序,他具体是如何实现的呢?其实我们在前面的知识中已经发现了,就是如果我们建的是小那小顶元素一定是整个里面最小的,大顶元素一定是最大的,那么如果我们想排一个顺序的方法就出来了,先把数组弄成一个大,再把第一个元素和最后一个元素交换,然后这个时候我们就排好了最大的一个,接着我们不管最后一个元素,对前面的n-1个元素再次建成大(其实只要进行向下调整就可以了因为下面的已经是大了),再重复上述操作。

那么我们就可以试着用代码实现了。

//建大
void ADjustDown(int* a, int parent, int n)
{int child = parent * 2 + 1;//子节点和双亲节点的关系while (child < n){if (child + 1 < n && a[child + 1] > a[child])//选出两个孩子中较大的一个{child++;}if (a[child] > a[parent])//若子节点比双亲节点大{Swap(&a[child], &a[parent]);//交换接着向下进行parent = child;child = parent * 2 + 1;}else{break;}}
}
void Heapsort(int* a, int n)
{int i = (n - 1 - 1) / 2;while (i >= 0){ADjustDown(a, i, n);i--;}//建while (n > 0){n--;Swap(&a[0], &a[n]);//交换首尾元素位置ADjustDown(a, 0, n);//下面都是建好的,只要向下调整就可以}
}
int main()
{int a[10] = { 3,5,9,87,6,12,7,11,13,1 };Heapsort(a,10);
}

✍5.结束语

好了小赵今天的分享就到这里了,如果大家有什么不明白的地方可以在小赵的下方留言哦,同时如果小赵的博客中有什么地方不对也希望得到大家的指点,谢谢各位家人们的支持。你们的支持是小赵创作的动力,加油。

如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持小赵,如有不足还请指点,方便小赵及时改正,感谢大家支持!!!


http://www.ppmy.cn/server/46585.html

相关文章

Python课设-学生信息管理系统

一、效果展示图 二、前端代码 1、HTML代码 <1>index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

基于Qt GraphicView 解析 CIM/G 电力接线图文件

本文讲述了如何使用Qt的框架来渲染展示标准的CIM/G格式的图形文件&#xff0c;也就是公用信息模型&#xff08;common information model&#xff0c;CIM&#xff09;中的G文件部分的内容。这是一种电力系统图形的交换规则&#xff0c;用于电网图形交换。 [by amjieker] CIM/G …

【头歌】计算机网络DHCP服务器配置第二关access口配置答案

头歌计算机网络DHCP服务器配置第二关access口配置操作步骤 任务描述 本关任务&#xff1a;创建 vlan &#xff0c;并且将与 pc 机相连接口划分 vlan 。 操作要求 在第一关的拓扑图的基础上&#xff0c;配置交换机&#xff0c;具体要求如下&#xff1a; 1、在特权模式下进入 vla…

Ubuntu20.04安装VINS_Mono 和 VINS_Fusion

文章目录 一、问题描述二、依赖环境1. Eigen 安装2. glog 安装3. gflags 安装4. ceres 安装 三、VINS-Mono 安装1. git 下载并安装2. OpenCV 版本冲突3. 运行 四、VINS—Fusion 安装1. git 下载并安装2. OpenCV 版本冲突3. 运行 五、日常bug1. 动静态库链接冲突 一、问题描述 …

自动化办公02 用openpyxl库操作excel.xlsx文件(新版本)

目录 一、文件读操作 二、文件写操作 三、修改单元格样式 openpyxl 是一个处理Excel表格的第三方库。openpyxl 库可以处理Excel2010以后的电子表格格式&#xff0c;包括&#xff1a;xlsx/xlsm/xltx/xltm。 openpyxl教程 一、文件读操作 工作簿(workbook): excel文件 工作表…

IDEA+MyBatisX根据mapper方法自动添加注解和生成xml方法结构

前提&#xff1a;确保IDEA已安装并启用了MyBatisX插件 在service层写dao或mapper的方法结构&#xff0c;反向生成dao层方法声明&#xff0c;如下&#xff1a; void updateStock(Long skuId, Long wareId, Integer skuNum); 由于该方法传递多个参数&#xff0c;为了让MyBatis识…

c# 学习教程

打印语句 折叠代码 变量 整形 浮点型 特殊类型

SQL入门教程

SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是一种用于管理和操作关系数据库的标准编程语言。以下是一些SQL入门的基本概念和步骤&#xff1a; 1. SQL基础 数据库&#xff08;Database&#xff09;&#xff1a;存储数据的容器。表&…