数据结构 | 详解二叉树——堆与堆排序

🥝堆

堆总是一棵完全二叉树。

大堆:父节点总是大于子节点。

小堆:父节点总是小于子节点。

注意:1.同一个节点下的两个子节点并无要求先后顺序。

           2.堆可以是无序的。

🍉堆的实现

🌴深度剖析

1.父节点和子节点之间的关系

子节点=(父节点*2)+1

或者子节点=(父节点*2)+2

父节点=(子节点-1)/2

2.堆的插入HeapPush实现

先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆
void HeapPush(Heap* php, HPDataType x) {assert(php);if (php->size == php->capacity) {int newcapacity = 0 ? 4 : 2 * php->capacity;HPDataType* tmp = (HPDataType*)malloc(sizeof(HPDataType) * newcapacity);if (tmp == NULL) {perror("malloc fail!");}php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;AdjustUp(php->a,php->size);php->size++;
}

3.堆的删除HeapPop函数的实现

函数目的:删除堆顶元素

为了避免破坏堆的整体结构,先将首尾元素进行交换,再对首元素进行向下调整,直到满足堆。最后php->size--即可删除原栈顶元素。

void HeapPop(Heap* php) {assert(php);swap(&php->a[0], &php->a[php->size - 1]);AdjustDown(php->a, php->size,0);php->size--;
}

🥳代码实现

Heap.h

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}Heap;void HeapInit(Heap* php);
// 堆的销毁
void HeapDestory(Heap* php);
// 堆的插入
void HeapPush(Heap* php, HPDataType x);
// 堆的删除
void HeapPop(Heap* php);
// 取堆顶的数据
HPDataType HeapTop(Heap* php);
// 堆的数据个数
int HeapSize(Heap* php);
// 堆的判空
int HeapEmpty(Heap* php);

Heap.c

#define _CRT_SECURE_NO_WARNINGS
#include "Heap.h"
void HeapInit(Heap* php) {assert(php);php->a = NULL;php->capacity = php->size = 0;
}void HeapDestory(Heap* php) {assert(php);free(php->a);php->a = NULL;php->capacity = php->size = 0;
}void swap(int* a, int* b) {int tmp = *a;*a= *b;*b = tmp;
}
//小堆
void AdjustUp(HPDataType* a,int child) {assert(a);int parent = (child - 1) / 2;while (child > 0) {if (a[parent] > a[child]) {swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else {break;}}
}void HeapPush(Heap* php, HPDataType x) {assert(php);if (php->size == php->capacity) {int newcapacity = 0 ? 4 : 2 * php->capacity;HPDataType* tmp = (HPDataType*)malloc(sizeof(HPDataType) * newcapacity);if (tmp == NULL) {perror("malloc fail!");}php->a = tmp;php->capacity = newcapacity;}php->a[php->size] = x;AdjustUp(php->a,php->size);php->size++;
}
//从给定的子节点开始,不断向上与其父节点进行比较和可能的交换,直到达到根节点或找到一个满足最大堆性质的父节点为止。
void AdjustDown(int* a, int n, int parent) {assert(a);int child = parent * 2 + 1;while (child < n) {if (child + 1 < n && a[child] < a[child + 1]) {child++;}if (a[parent] < a[child]) {swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else {break;}}
}void HeapPop(Heap* php) {assert(php);swap(&php->a[0], &php->a[php->size - 1]);AdjustDown(php->a, php->size,0);php->size--;
}HPDataType HeapTop(Heap* php) {assert(php);assert(php->size > 0);return php->a[0];
}int HeapSize(Heap* php) {assert(php);return php->size;
}int HeapEmpty(Heap* php) {assert(php);return php->size;
}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include "Heap.h"
int main() {Heap hp;HeapInit(&hp);HeapPush(&hp, 7);HeapPush(&hp, 6);HeapPush(&hp, 5);HeapPush(&hp, 4);HeapPush(&hp, 3);HeapPush(&hp, 2);HeapPush(&hp, 1);for (int i = 0; i < hp.size; i++) {printf("%d ", hp.a[i]);}HeapPop(&hp);printf("\n");for (int i = 0; i < hp.size; i++) {printf("%d ", hp.a[i]);}printf("\n");printf("堆顶元素为%d\n", HeapTop(&hp));if (HeapEmpty(&hp)) {printf("堆不为空\n");}else {printf("堆为空\n");}return 0;
}

🍇堆排序

🌴深度剖析

第一步:建堆

(升序建大堆,降序建小堆)

以升序为例:

从最后一个父节点开始向前遍历,向上调整(大的上小的下)。

	//建堆:从倒数第一个父节点开始向前遍历,向下调整for (int i = (n-1-1)/2; i >=0 ;i--) {AdjustDown(a,n,i);}

第二步:排序

1.首尾元素交换(左图)

2.再向下调整(大的上小的下),这样调整后的堆顶元素必为调整范围内的最大值,经过下一轮的首尾元素交换后,就可以放入调整完的区域内。

while (n - 1) {swap(&a[0], &a[n - 1]);AdjustDown(a, n-1,0);n--;

🥳代码实现

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>void swap(int* a, int* b) {int tmp = *a;*a = *b;*b = tmp;
}void AdjustUp(int* a, int child) {assert(a);int parent = (child - 1) / 2;while (child > 0) {if (a[parent] < a[child]) {swap(&a[parent], &a[child]);child = parent;parent = (child - 1) / 2;}else {break;}}
}
void AdjustDown(int* a, int n, int parent) {assert(a);int child = parent * 2 + 1;while (child < n ) {if (child + 1 < n  &&  a[child] < a[child + 1]) {child++;}if (a[parent] < a[child]) {swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else {break;}}
}//升序建大堆 降序建小堆
void HeapSort(int* a, int n) {//建堆:从倒数第一个父节点开始向前遍历,向下调整for (int i = (n-1-1)/2; i >=0 ;i--) {AdjustDown(a,n,i);}//先将首尾元素进行交换,再向下调整while (n - 1) {swap(&a[0], &a[n - 1]);AdjustDown(a, n-1,0);n--;}
}int main() {int a[7] = { 2,6,5,1,7,4,3 };int n = sizeof(a) / sizeof(a[0]);HeapSort(a, n);for (int i = 0; i < n; i++) {printf("%d ",a[i]);}return 0;
}

🍉从时间复杂度角度分析建堆为何采取向下调整?

下面将分别分析向下调整算法建堆和向上调整算法建堆的区别:

向下调整建堆

假设节点数量为N,树的高度为h

第一层,2^0个节点,需要向下调整h-1层

第二层,2^1个节点,需要向下调整h-2层

第三层,2^2个节点,需要向下调整h-3层

……

第h层,2^h个节点,需要向下调整0层

可以看出:节点少的层向下调整得多,节点多的层向下调整得少

计算向下调整建堆最坏情况下合计的调整次数:

通过错位相减法可得:

因此向下调整建堆的时间复杂度为O(N)

向上调整建堆:

假设节点数量为N,树的高度为h

第一层,2^0个节点,需要向下调整0层

第二层,2^1个节点,需要向下调整1层

第三层,2^2个节点,需要向下调整2层

……

第h层,2^h个节点,需要向下调整h-1层

可以看出:节点少的层向上调整得少,节点多的层向上调整得多。

T(h)=2^1*1+2^2*2+……+2^(h-2)*(h-2)+2^(h-1)*(h-1)

同样由错位相减法可得:

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

整理可得:

T(N)=-N+(N+1)*(log2(N+1)-1)+1

因此向上调整建堆的时间复杂度为O(N*logN)

所以我们选择向下建堆算法明显效率更高。

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

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

相关文章

Gir clone 设置代理与错误

git查看、配置、删除代理 link git config --global https.proxy http://127.0.0.1:1080 git config --global http.proxyhttps://stackoverflow.com/questions/11265463/reset-git-proxy-to-default-configuration git config --global --unset http.proxy git config --gl…

Spring-注解

Spring 注解分类 Spring 注解驱动模型 Spring 元注解 Documented Retention() Target() // 可以继承相关的属性 Inherited Repeatable()Spirng 模式注解 ComponentScan 原理 ClassPathScanningCandidateComponentProvider#findCandidateComponents public Set<BeanDefin…

数据源不同?奥威BI软件是这么做的

面对数据源不同的情况&#xff0c;BI&#xff08;商业智能&#xff09;软件如奥威BI软件通常通过一系列技术和方法来实现数据的整理。以下以奥威BI软件为例&#xff0c;详细解释其如何整理不同数据源的数据&#xff1a; 数据收集&#xff1a; 爬虫技术&#xff1a;奥威BI软件…

Java程序设计

一 Java基础知识 1 Java语言概述 1.1 发展历史 1.2 Java应用领域 Web开发&#xff1a;电子商务网站、内部管理系统、社交网络、门户网站移动开发&#xff1a;Android开发桌面开发&#xff1a;办公软件、游戏、工具软件企业应用开发&#xff1a;客户关系管理、企业资源计划、…

【数据结构】探索树中的奇妙世界

专栏介绍&#xff1a; 哈喽大家好&#xff0c;我是野生的编程萌新&#xff0c;首先感谢大家的观看。数据结构的学习者大多有这样的想法&#xff1a;数据结构很重要&#xff0c;一定要学好&#xff0c;但数据结构比较抽象&#xff0c;有些算法理解起来很困难&#xff0c;学的很累…

redis 集群 底层原理以及实操

前言 上篇我们讲解了哨兵集群是怎么回事 也说了对应的leader选举raft算法 也说了对应的slave节点是怎么被leader提拔的 主要是比较优先级 比较同步偏移量 比较runid等等 今天我们再说说,其实哨兵也有很多缺点 虽然在master挂了之后能很快帮我们选举出新的master 但是对于单个ma…

C#解析JSON的常用库--Newtonsoft.Json

一、库介绍 在C#中&#xff0c;解析JSON的常用库有Newtonsoft.Json&#xff08;也称为Json.NET&#xff09;和 System.Text.Json&#xff08;从 .NET Core 3.0 开始引入&#xff09;。本文主要介绍 Newtonsoft.Json。 二、下载 官网&#xff1a; https://www.nuget.org/pack…

解决文件传输难题:如何绕过Gitee的100MB上传限制

引言 在版本控制和代码托管领域&#xff0c;Gitee作为一个流行的平台&#xff0c;为用户提供了便捷的服务。然而&#xff0c;其对单个文件大小设定的100MB限制有时会造成一些不便。 使用云存储服务 推荐理由&#xff1a; 便捷性&#xff1a;多数云存储服务如&#xff1a; Dro…

Vue——事件修饰符

文章目录 前言阻止默认事件 prevent阻止事件冒泡 stop 前言 在官方文档中对于事件修饰符有一个很好的说明&#xff0c;本篇文章主要记录验证测试的案例。 官方文档 事件修饰符 阻止默认事件 prevent 在js原生的语言中&#xff0c;可以根据标签本身的事件对象进行阻止默认事件…

隆道出席河南ClO社区十周年庆典,助推采购和供应链数字化发展

5月26日&#xff0c;“河南ClO社区十周年庆典”活动在郑州举办&#xff0c;北京隆道网络科技有限公司总裁助理姚锐出席本次活动&#xff0c;并发表主题演讲《数字化采购与供应链&#xff1a;隆道的探索与实践》&#xff0c;分享隆道公司在采购和供应链数字化转型方面的研究成果…

ZooKeeper安装

安装Zookeeper 1、下载Zookeeper安装包 打开链接选择一个版本进行下载 https://zookeeper.apache.org/releases.html2、上传Zookeeper安装包到集群 输入命令 scp apache-zookeeper-3.8.4-bin.tar.gz hadoop192.168.88.100:/tmp也可以使用xftp等上传&#xff0c;物理机用u盘…

方法的重写--5.29

当子类对父类的方法不满意时&#xff0c;可以进行重写&#xff0c;但是方法名字要与父类一样。 举例&#xff0c;我用people来举例&#xff0c;我是打工人&#xff0c;然后再创一个student类&#xff0c;重写方法我不是打工人&#xff0c;我是读书人。代码如下&#xff0c;发现…

如何让你的网站能通过域名访问

背景 当我们租一台云服务器&#xff0c;并在上面运行了一个Web服务&#xff0c;我们可以使用云服务器的公网IP地址进行访问&#xff0c;如下&#xff1a; 本文主要记录如何 实现让自己的网站可以通过域名访问。 买域名 可以登录腾讯云等主流公有云平台的&#xff0c;购买域名…

Matlab|基于PMU相量测量单元进行电力系统电压幅值和相角状态估计

主要内容 程序采用三种方法对14节点和30节点电力系统状态进行评估&#xff1a; ①PMU同步相量测量单元结合加权最小二乘法&#xff08;WLS&#xff09;分析电力系统的电压幅值和相角状态&#xff1b; ②并采用牛顿-拉夫逊方法进行系统潮流计算&#xff0c;结果作为理论分…

摩尔线程MTT S4000 AI GPU助力30亿参数大模型训练,性能比肩英伟达同类解决方案

中国国产GPU制造商摩尔线程(Moore Threads)在AI加速器领域取得了显著进展&#xff0c;其最新推出的MTT S4000 AI GPU在训练大规模语言模型时表现突出&#xff0c;据称相较于其前代产品有着显著的性能提升。根据cnBeta的报道&#xff0c;搭载S4000 GPU的全新“酷鹅千卡智能计算集…

技术面试,项目实战,求职利器

之前找工作一直想找一个能真正系统性学开发的地方&#xff0c;之前毕业找工作的时候无意间碰到下面这个网站&#xff0c;感觉还挺不错的&#xff0c;用上面的技术实战内容应对技术面试&#xff0c;也算是求职利器了。有需要的可以自取&#xff1a; https://how2j.cn?p156336 实…

springboot课程题库管理系统-计算机毕业设计源码30812

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于课程题库管理系统 当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了课程题库管理系统 &#xff0c;它彻底改变了…

基于DdddOcr通用验证码离线本地识别SDK搭建个人云打码接口Api

前言 最近介绍了一款免费的验证码识别网站,识别效率太低,考虑到ddddocr是开源的,决定搭建搭建一个,发现原作者sml2h3已经推出好久了,但是网上没有宝塔安装的教程,于是本次通过宝塔搭建属于自己的带带弟弟OCR通用验证码离线本地识别 原项目地址:https://github.com/sml2…

Linux-应用编程学习笔记(二、文件I/O、标准I/O)

一、文件I/O基础 文件 I/O 指的是对文件的输入/输出操作&#xff0c;就是对文件的读写操作。Linux 下一切皆文件。 1.1 文件描述符 在 open函数执行成功的情况下&#xff0c; 会返回一个非负整数&#xff0c; 该返回值就是一个文件描述符&#xff08;file descriptor&#x…

搭建YOLOv10环境 训练+推理+模型评估

文章目录 前言一、环境搭建必要环境1. 创建yolov10虚拟环境2. 下载pytorch (pytorch版本>1.8)3. 下载YOLOv10源码4. 安装所需要的依赖包 二、推理测试1. 将如下代码复制到ultralytics文件夹同级目录下并运行 即可得到推理结果2. 关键参数 三、训练及评估1. 数据结构介绍2. 配…