深入浅出堆—C语言版【数据结构】

news2024/9/25 19:25:48

二叉树概念博客:http://t.csdn.cn/XIW84

目录

1. 了解堆

1.1 堆的概念

1.2 堆的性质:

1.3 堆的结构图片

1.3.1 小堆

1.3.2 大堆

2. 堆的实现

2.1 插入数据进堆

2.2 向上调整函数

2.3 堆的删除

2.4 向下调整

3. 堆的应用

3.1 建堆(两种方式)

3.1.1 建堆方式1

3.1.2 建堆方式2

3.2 堆排序 

3.3 堆的TOP—K问题


1. 了解堆

1.1 堆的概念

1.2 堆的性质:

堆中某个节点的值总是不大于或不小于其父节点的值;

堆总是一棵完全二叉树。

1.3 堆的结构图片

1.3.1 小堆

 满足下面条件的是小堆

1.3.2 大堆

满足下面条件的是大堆

 注意不一定是从大到小、从小到大存储的!!!


堆有什么作用呢?

下面来细讲,别走开!!!



2. 堆的实现

2.1 插入数据进堆

void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType)*newcapacity);
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}

		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

注意点!!!

假如一开始我们的堆是小堆,但是在插入数据以后要保持还是小堆,要将插入的数据的大小和它的父亲进行比较,比较的两种情况:

1. 如果插入的数据比父亲还要大,那就不需要调整

2. 如果插入的数据比父亲还要小,那就需要调整   

  如果需要调整,我们就要使用向上调整算法,保持插入数据后的堆还是小堆


2.2 向上调整函数

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;//求出插入数据的父亲位置下标
	
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;//将父亲的下标给孩子,向上调整
			parent = (child - 1) / 2;//再算出此时插入数据的父亲下标
		}
		else
		{
			break;
		}
	}
}

2.3 堆的删除

能不能使用覆盖删除呢—不能!!!

使用覆盖删除,会打乱父子之间的下标关系,父子关系就会全部乱掉,因此我们使用下面的方法来删除数据


1. 先将下标为0位置的数据和下标最大的数据进行交换

2. 然后直接size--

3. 然后还需要使用向下调整算法,把堆再调整为小堆

void HeapPop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	Swap(&(php->a[0]), &(php->a[php->size - 1]));1.交换
	php->size--;//2. 删除堆顶元素

	AdjustDwon(php->a, php->size, 0);//向下调整,保证还是小堆
}

2.4 向下调整

void AdjustDwon(HPDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		// 选出左右孩子中小那个
        //这里的if里面的判断大小尽量写成小于是小堆,大于是大堆
		if (child+1 < size && a[child+1] < a[child])
		{
			++child;
		}

		// 孩子跟父亲比较
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}	
}


3. 堆的应用

3.1 建堆(两种方式)

3.1.1 建堆方式1

利用插入元素的方式,向上调整建堆 

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	
	while (child > 0)
	{
		//if (a[child] < a[parent])
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
/

void HeapSort(int* a, int n)//传一个数组过来,还有元素个数
{
	// 建堆方式1:O(N*logN)
	for (int i = 1; i < n; ++i)
	{
		AdjustUp(a, i);//从插入的第二个元素开始
	}
}

建堆方式1的时间复杂度 ——错位相减法


3.1.2 建堆方式2

利用向下调整建堆

方法:找到最后一个元素的父亲,并从这个位置开始向下调整                                    

void HeapSort(int* a, int n)
{

	// 建堆方式2:O(N)
	for (int i = (n-1-1)/2; i >= 0; --i)
	{
		AdjustDwon(a, n, i);
	}

	// O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon(a, end, 0);
		--end;
	}
}

建堆方式2的时间复杂度——错位相减法



3.2 堆排序 

排升序,建大堆,再向下调整

为什么建大堆呢?

建大堆,堆顶元素是最大的数,让堆顶元素和最后一个元素交换,再向下调整,注意:这里向下调整时是调整的数组大小-1个,也就是调整刚刚交换下来前面的数据


排降序,建小堆,再向下调整

void HeapSort(int* a, int n)
{

	// 建堆方式2:O(N)
	for (int i = (n-1-1)/2; i >= 0; --i)
	{
		AdjustDwon(a, n, i);
	}

	// O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);//这里的end是9,传过去向下调整的元素个数也是9,
                             //就不会调整刚刚从堆顶传下来的数据
		AdjustDwon(a, end, 0);
		--end;
	}

3.3 堆的TOP—K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

实现思路: 

这样空间复杂度非常小


注意:

           求前k个最大的数,是建小堆

           解释:由于建立的前k个数是小堆,后面n-k个数都可能比对顶的数值大,比堆顶的元素大,就替换堆顶的元素,然后再向下调整,保持前k个数是小堆,然后再比较····

           求前k个最小的数,是建大堆(同上


 代码实现:

void PrintTopK(int* a, int n, int k)
{
	// 1. 建堆--用a中前k个元素建堆
	int* kMinHeap = (int*)malloc(sizeof(int)*k);
	assert(kMinHeap);
	for (int i = 0; i < k; ++i)//将a数组里面前10个数赋值给KMinHeap
	{
		kMinHeap[i] = a[i];
	}
	for (int i = (k - 1 - 1) / 2; i >= 0; --i)//向下调整建堆,建立k个数的小堆
	{
		AdjustDwon(kMinHeap, k, i);
	}

	// 2. 将剩余n-k个元素依次与堆顶元素交换,不满则则替换
	for (int j = k; j < n; ++j)
	{
		if (a[j] > kMinHeap[0])
		{
			kMinHeap[0] = a[j];
			AdjustDwon(kMinHeap, k, 0);//再向下调整,保持前k个数是小堆
		}
	}

	for (int i = 0; i < k; ++i)
	{
		printf("%d ", kMinHeap[i]);
	}
	printf("\n");
}

void TestTopk()
{    
    //随机生成一万个数字,每个数字%1百万,这一万都是比一百万小的数字,
    //我们将其中的10个数改为比一百万大的值
	int n = 10000;
	int* a = (int*)malloc(sizeof(int)*n);
	srand(time(0));
	for (int i = 0; i < n; ++i)
	{
		a[i] = rand() % 1000000;
	}
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[120] = 1000000 + 5;
	a[99] = 1000000 + 6;
	a[0] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;

	PrintTopK(a, n, 10);
}


本文讲的是二叉树的顺序存储结构(堆)的实现,下期我们来讲二叉树的链式存储结构,到时候记得来支持小余哦!!!

如果觉得文章不错,期待你的一键三连哦,你个鼓励是我创作的动力之源,让我们一起加油,顶峰相见!!!

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

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

相关文章

赚钱单页产品

今天写一篇文章&#xff0c;讲一个最近看的赚钱单页产品。 先下定义&#xff0c;什么是赚钱的单页产品&#xff1a; 能赚钱&#xff1a;需求切的准单页产品&#xff1a;通常只有少数几个页面就完成了产品的核心功能&#xff0c;一个程序员可以在1天左右&#xff0c;完成开发 先…

快速理解基本的cookie、session 和 redis

一、Cookie 1、什么是Cookie 1、Cookie实际上是一小段的文本信息&#xff0c;是一种keyvalue形式的字符串。客户端请求服务器&#xff0c;如果服务器需要记录该用户状态&#xff0c;就使用response向客户端浏览器颁发一个Cookie。客户端会把Cookie保存起来。 2、当浏览器再请求…

跨境电商系统开发需要注意的问题

跨境商城系统开发需要考虑许多特殊问题&#xff0c;比如涉及跨国支付、物流、法律和政策、文化差异等等。下面将列出一些重要的问题&#xff0c;来帮助您更有效地开发跨境电商系统。 1、税务问题 当涉及跨国交易时&#xff0c;涉及不同国家和地区的税收政策和税率。特别是在国…

2023亚马逊云科技中国峰会开启报名 6月27-28日上海见

2023年6月27-28日&#xff0c;2023亚马逊云科技中国峰会&#xff0c;亚马逊云科技将聚焦构建新技术加速的产品创新、新架构提升的业务弹性、云计算引领的创新模式&#xff0c;围绕价值加速实现&#xff0c;企业敏捷稳健&#xff0c;行业全新机遇等热门话题&#xff0c;携手众多…

( 位运算 ) 136. 只出现一次的数字 ——【Leetcode每日一题】

❓136. 只出现一次的数字 难度&#xff1a;简单 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量…

学习dtw-python库内容 动态弯曲距离(DTW)具体实现

文章目录 一、install 数据包二、函数功能三、函数的参数以及含义四、具体实现 一、install 数据包 简单的pip install一下就好了&#xff0c;注意最后提示Successfully installed dtw-python-1.3.0 pip install dtw-python二、函数功能 执行 DTW 算法&#xff0c;并计算两个…

网页端操作提示「msg.js」库简介

这段时间我正在完成我的第一本个人图书&#xff0c;期间做了很多的案例&#xff0c;最近需要在网页端完成一个关于「恶意文本检测」的案例&#xff0c;为了让该案例表现的更加易用简洁、对用户友好&#xff0c;我需要在页面中添加一些用户操作提示信息&#xff0c;比如「正在加…

【HMS Core】Health Kit想要查看数据是来自用户的哪个设备,如何查看?

【问题描述1】 如何查看运动健康数据是来自用户的哪个设备&#xff1f; 【解决方案】 可以通过返回的数据中携带的dataCollectorId来查询提供数据的设备信息&#xff1a; 请求示例&#xff08;以查询睡眠记录详情为例&#xff09;&#xff1a; 1、查询睡眠记录并关联睡眠状…

后端使用phantomjs对页面进行截图

最近碰到这样一些需求&#xff0c;后端需要对某个图表页面进行动态截图&#xff0c;将截图通过邮件发送到指定邮箱进行每日提醒。 这就需要用到无界浏览器进行此类操作。常见的无界浏览器有以下几种&#xff0c;知识来源于chatgpt3.5&#xff1a; Headless Chrome - Google C…

NC – 靶向特定功能的神经元细胞类型治疗脑部疾病

神经元是大脑的主要功能单位。这些细胞中传递的信号——以电波的形式——导致所有思维、感觉、运动、记忆和情感。 塞达斯-西奈医学中心的研究人员利用计算机模型来弥合“试管”神经元数据和这些细胞在大脑中的功能之间的差距。他们的研究有助于开发靶向特定功能的神经元类型治…

如何吸引主流媒体报道,强势刷屏?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好 五一小长假已经过去好几天了&#xff0c;大家渐渐的从假期舒适的闲暇转到正常的工作生活中了&#xff0c;但利用五一热点宣传的余温还在继续&#xff0c;今天胡老师就i注意到一篇题为&#xff1a;【强势刷屏&#xff01;“…

【笔记】【HTTP】《图解HTTP》第2章 简单的HTTP协议

前言 有输入就要有产出&#xff0c;该笔记是本人看完《图解HTTP》后对每章涉及到的知识进行汇总博客将会已书的每章为一篇发布&#xff0c;下一篇博客发布时间不确定笔记中有些个人理解后整理的笔记&#xff0c;可能有所偏差&#xff0c;也恳请读者帮忙指出&#xff0c;谢谢。…

Java每日一练(20230509) 下一个排列、分隔链表、随机指针链表

目录 1. 下一个排列 &#x1f31f;&#x1f31f; 2. 分隔链表 &#x1f31f;&#x1f31f; 3. 复制带随机指针的链表 &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日…

操作系统—— 精髓与设计原理--期末复习

一、计算机系统概述 1、基本构成 计算机有四个主要的结构化部件&#xff1a; ①处理器&#xff08;Processor&#xff09;&#xff1a;控制计算机的操作&#xff0c;执行数据处理功能。当只有一个处理器时&#xff0c;它通常指中央处理器&#xff08;CPU&#xff09; ②内存…

VINS_FUSION的EVO评价

一、虚拟机和windows桌面可以复制粘贴的方法 1&#xff09;sudo apt-get autoremove open-vm-tools 2&#xff09;sudo apt-get insall open-vm-tools-desktop 3&#xff09;reboot 二、算法精度评价工具EVO 安装参考博文&#xff1a; 视觉SLAM基础&#xff1a;算法精度评…

计算机二级java经典题目及其解析

解析: 栈的存储空间为S(1:50)&#xff0c;初始状态为top51&#xff0c;表示栈的可用空间从S(1)到S(50)&#xff0c;栈顶指针初始指向S(51)的位置&#xff0c;表示当前栈为空。 经过一系列正常的入栈与退栈操作后&#xff0c;top50&#xff0c;表示栈顶指针已经向下移动了一个位…

微信小程序学习实录6(百度经纬度采集、手动调整精度、H5嵌入小程序、百度地图jsAPI、实时定位、H5更新自动刷新)

百度经纬度采集 一、H5页面开发1.手机端外部JS库2.地图容器3.数据表单4.地图加载5.回调封装函数自动定位 二、微信小程序核心代码1.lnglat.wxml2.lnglat.js3.lnglat.json 三、版本发布遇见的问题 一、H5页面开发 1.手机端外部JS库 viewport&#xff0c;手机端的适配&#xff…

【软考数据库】第九章 非关系型数据库NOSQL

目录 9.1 概述 9.2 理论基础 9.3 分区方法 9.4 存储分布 9.5 查询模型 9.6 存储模式 前言&#xff1a; 笔记来自《文老师软考数据库》教材精讲&#xff0c;精讲视频在b站&#xff0c;某宝都可以找到&#xff0c;个人感觉通俗易懂。 9.1 概述 传统的关系数据库在应付Web …

Listener监听器,实现一个显示在线用户人数

Listener监听器&#xff0c;实现一个显示在线用户人数 每博一文案 关于后半身&#xff0c;脾气越温&#xff0c;福报越深。 师傅说&#xff1a;惜命最好的方式不是养生&#xff0c;而是管好自己的情绪。 坏毛病都是惯出来的&#xff0c;但好脾气都是磨出来的&#xff0c;与人生…

《小钊记》项目启动前期工作相关记录:VUE、powerdesigner建模、虚拟机密码重置、代码生成

目录 VUE镜像基本命令vue 不是内部或外部命令路径配置路由 powerdesigner 建模栏位添加注释id设置自增导出sql 虚拟机root密码重置&#xff08;centos7&#xff09;生成代码工具安装EasyCode插件连接数据库生成代码可以自定义模板复制现有的模板&#xff0c;在其基础上进行改造…