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

🥝堆

堆总是一棵完全二叉树。

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

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

注意: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.mzph.cn/web/18950.shtml

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

相关文章

QT截图程序,可多屏幕截图二,增加调整截图区域功能

上一篇QT截图程序&#xff0c;可多屏幕截图只是实现了最基本的截图功能&#xff0c;虽然能用但是缺点也有&#xff0c;没办法更改选中的区域&#xff0c;这在实际使用时不太方便。这篇增加了这个功能。先看看效果。 实现代码为&#xff1a; 头文件 #ifndef MASKWIDGET_H #de…

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

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

LLAMA3==shenzhi-wang/Llama3-8B-Chinese-Chat。windows安装不使用ollama

创建环境&#xff1a; conda create -n llama3_env python3.10 conda activate llama3_env conda install pytorch torchvision torchaudio cudatoolkit11.7 -c pytorch 安装Hugging Face的Transformers库&#xff1a; pip install transformers sentencepiece 下载模型 ht…

开源一个工厂常用的LIMS系统

Senaite是一款强大且可靠的基于Web的LIMS/LIS系统&#xff0c;采用Python编写&#xff0c;构建在Plone CMS基础架构之上。该系统处于积极开发阶段&#xff0c;在灵活的定制空间中为开发人员提供了丰富的功能。其中&#xff0c;Senaite在处理REST的JSON API上做得出色&#xff0…

python -【二】判断语句

判断语句 一、 布尔类型 True&#xff1a;真(1)False&#xff1a;假(0) 比较运算符 运算符实例1 1 True!1 ! 1 Flase<1 < 1 Flse>1 > 1 Flse<1 < 1 True>1 > 1 True b1 True b2 False print(f"b1值是{b1},类型是{type(b1)}") print(…

【Linux】Socket中的心跳机制(心跳包)

Socket中的心跳机制(心跳包) 1. 什么是心跳机制&#xff1f;(心跳包) 在客户端和服务端长时间没有相互发送数据的情况下&#xff0c;我们需要一种机制来判断连接是否依然存在。直接发送任何数据包可以实现这一点&#xff0c;但为了效率和简洁&#xff0c;通常发送一个空包&am…

npm入门介绍

npm&#xff08;Node Package Manager&#xff09;是 Node.js 的包管理工具&#xff0c;用于安装、管理和共享 JavaScript 代码包。它是全球最大的软件注册表&#xff0c;拥有数百万个开源包&#xff0c;为开发者提供了丰富的资源和工具。以下是 npm 的详细介绍&#xff1a; 1…

vs工程添加自定义宏

一、简介 用户可以添加自定义宏变量方便工程路径名称的修改和配置 例&#xff1a;$(SolutionDir) 为解决方案路径&#xff0c;$(PojectDir) 为工程所在路径 测试环境&#xff1a;vs2017&#xff0c;qt5.14.0 二、配置 1、打开属性窗口&#xff1a;视图-》其他窗口-》属性管…

四数之和-力扣

本题在三数之和的基础上&#xff0c;再增加一重循环进行解答 首先注意的点是&#xff0c;一级剪枝处理&#xff0c;target > 0 && nums[i] > target 此处只有整数才可剪枝处理&#xff0c;如果target为负数&#xff0c;nums[i] < target&#xff0c;也不能代…

精准操控时间的艺术:JavaScript节流函数的深度探索与实践【含代码示例】

精准操控时间的艺术&#xff1a;JavaScript节流函数的深度探索与实践【含代码示例】 节流基础&#xff1a;概念与作用实现策略&#xff1a;案例展示案例一&#xff1a;基础定时器实现案例二&#xff1a;立即执行版本案例三&#xff1a;使用requestAnimationFrame实现动画节流 功…

蓝桥杯-AB路线(详细原创)

问题描述&#xff1a; 有一个由 N M 个方格组成的迷宫&#xff0c;每个方格写有一个字母 A 或者 B。小蓝站在迷宫左上角的方格&#xff0c;目标是走到右下角的方格。他每一步可以移动到上下左右相邻的方格去。 由于特殊的原因&#xff0c;小蓝的路线必须先走 K 个 A 格子、再…

前端面试题日常练-day40 【面试题】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末 1. Bootstrap 的栅格系统是基于&#xff08; &#xff09;进行布局的。A. 像素 B. 百分比 C. 媒体查询 2. 在 Bootstrap 中&#xff0c;要创建一个按钮&#xff0c;可以使用&#xff08; &#xff…

【CSharp】判断目录以及文件是否存在

【CSharp】判断目录以及文件是否存在 1.背景2.判断目录3.判断文件1.背景 我们在进行磁盘IO的时候进行需要判断目录、文件是否存在,根据判断结果再做进一步的操作。 其中判断目录是否存在,涉及Directory.Exists(String) 方法; 命名空间:System.IO 方法功能:确定给定路径是…

Spring OAuth2:开发者的安全盾牌!(下)

上文我们教了大家如何像海盗一样寻找宝藏&#xff0c;一步步解锁令牌的奥秘&#xff0c;今天将把更加核心的技巧带给大家一起学习&#xff0c;共同进步&#xff01; 文章目录 6. 客户端凭证与密码模式6.1 客户端凭证模式应用适用于后端服务间通信 6.2 密码模式考量直接传递用户…

(精确度,召回率,真阳性,假阳性)ACC、敏感性、特异性等 ROC指标

&#xff08;精确度&#xff0c;召回率&#xff0c;真阳性&#xff0c;假阳性&#xff09;ACC、敏感性、特异性等 ROC指标

【微机原理及接口技术】可编程计数器/定时器8253

【微机原理及接口技术】可编程计数器/定时器8253 文章目录 【微机原理及接口技术】可编程计数器/定时器8253前言一、8253的内部结构和引脚二、8253的工作方式三、8253的编程总结 前言 本篇文章就8253芯片展开&#xff0c;详细介绍8253的内部结构和引脚&#xff0c;8253的工作方…

E: Package ‘curl‘ has no installation candidate

错误1&#xff1a; E: Package curl has no installation candidate Package curl is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or is only available from another sourceE: Package curl …

人工智能初识

&#x1f31e;欢迎来到人工智能基础的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f4c6;首发时间&#xff1a;&#x1f339;2024年5月1…

C/C++语法|pthread线程库的使用

笔记主要内容来自 爱编程的大柄–线程 爱编程的大柄–线程同步 在进入代码实践之前&#xff0c;我们应该搞清楚。 线程是成语的最小执行单位&#xff0c;进程是操作系统中最小的资源分配单位。 这样的话我们可以理解以下两点&#xff1a; 同一地址空间中的多个线程独有的是&…

618手把手教你捡漏服务器

618最全捡漏攻略 捡漏规则1、新人优惠⭐⭐⭐2、教育优惠⭐⭐3、回馈活动⭐️ ECS价格对比新人优惠&#x1f49d;京东云 50/年百度云 60.69/年阿里云 82/年腾讯云 99/年 回馈活动&#x1f381;阿里云 教育优惠&#x1f3eb;阿里云腾讯云 hi&#xff0c;好久不见各位&#xff0c;…