【数据结构】堆的实现和排序

news2024/12/29 11:11:49

目录

1、堆的概念和结构

1.1、堆的概念

1.2、堆的性质

1.3、堆的逻辑结构和存储结构

2、堆的实现

2.1、堆的初始化和初始化

2.2、堆的插入和向上调整算法

2.3、堆的删除和向下调整算法

2.4、取堆顶的数据和数据个数

2.5、堆的判空和打印

2.6、测试

3、堆的应用

3.1、堆排序

3.1.1、建堆

3.1.2、排序

4、TOP-K问题


1、堆的概念和结构

1.1、堆的概念

简单理解堆是一个数组,可以把数组看成一个完全二叉树,根节点最大的堆是大根堆,根节点最小的堆是小根堆

1.2、堆的性质

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

(即树中所有的父亲都是<=孩子或者>=孩子)

堆是一棵完全二叉树

1.3、堆的逻辑结构和存储结构

堆的逻辑结构是一个完全二叉树,存储结构是数组

2、堆的实现

2.1、堆的初始化和初始化

void HeapInit(HP* php);
void HeapDestory(HP* php);

//堆的初始化
void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;//扩容error
	php->size = php->capacity = 0;
}
//堆的销毁
void HeapDestory(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = php->capacity = 0;
}

2.2、堆的插入和向上调整算法

在堆尾插入数据后,在插入的位置开始向上调整,依然保持堆结构

void HeapPush(HP* php,HPDataType x);

void AdjustUp(HPDataType* a, int child)
{
	assert(a);
	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;
		}
	}
}

void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		//扩容
		int NewCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HP* temp = (HP*)realloc(php->a,  NewCapacity*sizeof(HPDataType));
		if (temp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		php->a = temp;
		php->capacity = NewCapacity;
	}
	php->a[php->size] = x;
	php->size++;

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

2.3、堆的删除和向下调整算法

堆要删除一个数据,一般是删除堆顶的数据,如果直接删除堆顶的数据,那么堆的结构全部乱了,要重新建堆,时间复杂度高,并且没有用到堆的特性,具体做法是将堆顶的数据和堆尾的数据进行交换,再采用向下调整算法重新生成一个堆。

向下调整算法的前提是左右子树都是堆

void HeapPop(HP* php);

//     删除小堆堆顶的元素
//思路:选出左右孩子小的一个
//     小的孩子比父亲小,交换数据,继续向下调整,孩子如果比父亲大,调整结束
void AdjustDown(HPDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
//右孩子下标要小于数组的范围size
		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;
	}
}

//删除堆里的数据 还要保持大小堆
void HeapPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	Swap(&(php->a[0]), &(php->a[php->size-1]));
	php->size--;
	AdjustDown(php->a, php->size, 0);
}

2.4、取堆顶的数据和数据个数

HPDataType HeapTop(HP* php);//取堆顶的数据
int HeapSize(HP* php);    //堆的数据个数

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);

	return php->a[0];
}

int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

2.5、堆的判空和打印

bool HeapEmpty(HP* php);//堆的判空
void HeapPrint(HP* php);//堆的打印

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;//扩容error
	php->size = php->capacity = 0;
}

2.6、测试

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"

void HeapTest()
{
	HP hp;
	HeapInit(&hp);
	int a[] = { 27,15,19,18,28,34,65,49,25,37 };
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		HeapPush(&hp, a[i]);
	}
	printf("堆插入完成:");
	HeapPrint(&hp);

	//排升序 建小堆 将堆顶的数据回写到数组中
	//排降序 建大堆 将堆顶的数据回写到数组中
	int i = 0;
	while (!HeapEmpty(&hp))
	{
		//printf("%d ", HeapTop(&hp));
		//HeapPop(&hp);
		a[i++] = HeapTop(&hp);
		HeapPop(&hp);
	}
	printf("回写到数组的升序序列:");
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
int main()
{
	HeapTest();
}

//运行结果:
//堆插入完成:15 18 19 25 28 34 65 49 27 37
//回写到数组的升序序列:15 18 19 25 27 28 34 37 49 65

3、堆的应用

3.1、堆排序

3.1.1、建堆

建堆有两种方式:向上建堆和向下建堆

向下建堆的时间复杂度是O(n)

向上建堆的时间复杂度是O(n*logN)

3.1.2、排序

建堆完成后,排升序,建大堆,排降序,建小堆

假设要排升序,需要建立大堆,将堆顶的数据和堆尾的数据交换,再用向下调整算法重新将次大的数排到堆顶位置,依次循环上述步骤,最后得到升序的序列

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		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;
	}
}
void HeapSort(int* a, int n)
{
	for (int i = (n - 1 - 1) / 2;i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}
void HeapSortTest()
{
	int a[] = { 27,15,19,18,28,34,65,49,25,37 };
	HeapSort(a, sizeof(a) / sizeof(int));
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
}
int main()
{
	HeapSortTest();
	return 0;
}

//输出结果 15 18 19 25 27 28 34 37 49 65

4、TOP-K问题

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

思路1:堆排序 时间复杂度O(N*logN)

思路2:将这N个元素建成大堆, Top和Pop K次 时间复杂度 O(O(n)+k*logN);

假设有N个元素,N非常大,有100亿个元素,k比较小,为100,求找出N中的前100大的元素。

前面两种思路当数据量特别大时求解不太容易现实,100亿个整数要占用40G的内存(1G=1024MB=1024*1024kb=1024*1024*1024byte 1G占10亿字节左右),明显不合适。

思路:

1、选取前K个数建小堆

2、剩下的N-K个数每次和小堆堆顶的数据比较,如果比堆顶的数据大,则交换堆顶的数据,比完后,最后堆里的K个数,就是最大的前K个数。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<assert.h>
void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//向下调整算法
void AdjustDown(int* a, int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		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;
		}
	}
}
void TopK(int* a, int n, int k)
{
	int* KMinHeap = (int*)malloc(sizeof(int) * k);
	assert(KMinHeap);
	//选出n中前k个数
	for (int i = 0; i < k; i++)
	{
		KMinHeap[i] = a[i];
	}
	//对前k个数进行建堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(KMinHeap, k, i);
	}
	//n-k个数依次和堆顶数据进行交换
	for (int i = k; i < n; i++)
	{
		if (a[i] > KMinHeap[0])
		{
			//Swap(&KMinHeap[0], &a[i]);
			KMinHeap[0] = a[i];
			AdjustDown(KMinHeap, k, 0);
		}
	}
	//打印前k大的数
	for (int i = 0; i < k; i++)
	{
		printf("%d ", KMinHeap[i]);
	}
	printf("\n");
}
void TestTopK()
{
	int n = 10000;
	int* a = (int*)malloc(sizeof(int) * n);
	srand(time(0));
	for (int i = 0; i < n; i++)
	{
		a[i] = rand() % 100000;
	}
	a[5] = 100000 + 1;
	a[1231] = 100000 + 2;
	a[531] = 100000 + 3;
	a[5121] = 100000 + 4;
	a[115] = 100000 + 5;
	a[2335] = 100000 + 6;
	a[9999] = 100000 + 7;
	a[76] = 100000 + 8;
	a[423] = 100000 + 9;
	a[3144] = 100000 + 10;
	TopK(a, n, 10);
}
int main()
{
	TestTopK();
	return 0;
}
//输出结果:100001 100002 100003 100004 100005 100007 100009 100006 100008 100010

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

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

相关文章

AIGC之视频图片生成工具gen-2

最近无事时研究了一款图片和视频生成工具&#xff0c;先说结论&#xff1a; 1.可以生成视频&#xff0c;生成方式有三种 通过文本的方式生成视频可以通过图片的方式生成视频也可以通过图片文本的方式生成视频 2.可以通过文本描述的方式生成图片 3.生成的视频有瑕疵&#xf…

Eureka整合seata分布式事务

文章目录 前言一、Seata配置1.1、Seata下载1.2、修改conf目录中 flie.conf 文件1.3、修改conf目录中 registry.conf文件1.4、初始化seata数据库 二、微服务整合Seata2.1、父工程项目创建引入依赖 2.2、Eureka集群搭建2.3、搭建账户微服务2.3.1 新建seata-account-service微服务…

React全局状态管理

redux是一个状态管理框架&#xff0c;它可以帮助我们清晰定义state和处理函数&#xff0c;提高可读性&#xff0c;并且redux中的状态是全局共享&#xff0c;规避组件间通过props传递状态等操作。 快速使用 在React应用的根节点&#xff0c;需要借助React的Context机制存放整个…

安卓apk加固后重签名

背景 等保检测&#xff0c;安卓apk使用第三方加固后签名信息会丢失&#xff0c;需要我们重新进行签名 使用jarsigner签名遇到的问题 APP失效无法安装 如何解决签名失效 我们在这里使用Android SDK的apksigner进行签名 mac系统&#xff0c;apksigner 需要设置环境变量 1、…

leedcode刷题day2

题目&#xff1a; 根据这道题我的思路是用python首先将第一个值赋给a&#xff0c;然后将下一个值赋值给b在这里写一个循环计算下一个值是否等于a&#xff0c;不等于就进入数组当等于a的时候输出数组长度&#xff0c;然后比较数组长度输出最长长度对应的元素不过显然这很慢。 然…

【Linux】权限的深度解析

前言&#xff1a;在此之前我们学习了一些常用的Linux指令&#xff0c;今天我们进一步学习Linux下权限的一些概念 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:Linux的学习 &#x1f448; &#x1f4af;代码仓库:卫卫周大胖的学习日记&a…

行列转化【附加面试题】

在MySQL中&#xff0c;行列转换是一种常见的操作。它包括行转列和列转行两种情况。 行转列&#xff1a;行转列是将表中的某些行转换成列&#xff0c;以提供更为清晰、易读的数据视图。例如&#xff0c;假设我们有一个包含科目和分数的表&#xff0c;我们可以使用SUM和CASE语句…

一款轻量级、基于Java语言开发的低代码开发框架,开箱即用!

数字化时代&#xff0c;企业对于灵活、高效和安全的软件开发需求日益旺盛。为了满足这些需求&#xff0c;许多组织转向低代码技术&#xff0c;以寻求更具成本效益和创新性的解决方案。JNPF基础框架正是在这一背景下应运而生&#xff0c;凭借其私有化部署和100%源码交付的特性&a…

011:vue结合css动画animation实现下雪效果

文章目录 1. 实现效果2. 编写一个下雪效果组件 VabSnow.vue3. 页面使用4. 注意点 1. 实现效果 GIF录屏文件太卡有点卡&#xff0c;实际是很丝滑的 2. 编写一个下雪效果组件 VabSnow.vue 在 src 下新建 components 文件&#xff0c;创建VabSnow.vue组件文件 <template>…

C++系统笔记教程----vscode远程连接ssh

C系统笔记教程 文章目录 C系统笔记教程前言开发环境配置总结 前言 开发环境配置 Ubuntu20.24VScode 如果没有linux系统&#xff0c;但是想用其编译&#xff0c;可以使用ssh远程连接。 首先进入vscode,打开远程连接窗口&#xff08;蓝色的小箭头这&#xff09; 选择连接到主机…

三菱plc学习入门(创建属于自己的FB模块)

在现实生活中&#xff0c;往往会需要修改一些属于方便自己的库&#xff0c;1&#xff0c;自己创建的库方便自己使用与查看2&#xff0c;提高自己编程能力&#xff0c;3&#xff0c;保护自己的程序不被外人修改&#xff01;&#xff01;&#xff01;下面就让我来操作一下 导入需…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例4-4 label

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>label</title> </head><body> 性别: <label for"male">男</label> <input type"radio" name"sex&quo…

python的tabulate包在命令行下输出表格不对齐

用tabulate可以在命令行下输出表格。 from tabulate import tabulate# 定义表头 headers [列1, 列2, 列3]# 每行的内容 rows [] rows.append((张三,数学,英语)) rows.append((李四,信息科技,数学))# 使用 tabulate 函数生成表格 output tabulate(rows, headersheaders, tab…

线程同步--生产者消费者模型

文章目录 一.条件变量pthread线程库提供的条件变量操作 二.生产者消费者模型生产者消费者模型的高效性基于环形队列实现生产者消费者模型中的数据容器 一.条件变量 条件变量是线程间共享的全局变量,线程间可以通过条件变量进行同步控制条件变量的使用必须依赖于互斥锁以确保线…

【C语言】- 设置控制台文字颜色、大小和字体

【C语言】- 设置控制台标题、编码、文字颜色、大小和字体 文章目录 【C语言】- 设置控制台标题、编码、文字颜色、大小和字体1 - 设置控制台标题2 - 设置控制台编码3 - 设置控制台字体和大小参考链接 1 - 设置控制台标题 因为要用到 Windows API&#xff0c;所以需要包含头文件…

CHAPTER 9: 《DESIGN A WEB CRAWLER》第9章 《设计一个web爬虫》

CHAPTER 9: 《DESIGN A WEB CRAWLER》第九章 设计一个web爬虫 在本章中&#xff0c;我们将重点介绍网络爬虫设计&#xff1a;一种有趣而经典的系统设计 面试问题。 网络爬虫被称为机器人或蜘蛛。它被搜索引擎广泛用于发现网络上的新内容或更新内容。内容可以是网页、图像、视频…

python:一元线性回归模型案例分析

一、案例分析背景 案例: 中国全体居民的消费水平与经济发展数量关系的分析 提出问题&#xff1a; 改革开放以来&#xff0c;随着中国经济的快速发展&#xff0c;人民生活水平不断提高&#xff0c;居民的消费水平也在不断增长。研究中国全体居民的消费水平与经济发展的数量关系…

代码随想录算法训练营第31天 | 理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和

目录 理论基础 455.分发饼干 &#x1f4a1;解题思路 &#x1f4bb;实现代码 376. 摆动序列 &#x1f4a1;解题思路 # 情况一&#xff1a;上下坡中有平坡 # 情况二&#xff1a;数组首尾两端 情况三&#xff1a;单调坡度有平坡 &#x1f4bb;实现代码 53. 最大子序…

RTC讲解

RTC&#xff08;Real Time Clock&#xff09;实时时钟 RTC实时时钟本质上是一个独立的定时器。RTC模块拥有一组连续计数的32位无符号计数器&#xff0c;在相应软件配置下&#xff0c;可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。 RTC模块和时钟配…

uniapp APP接入Paypal

1. 登录paypal开发者中心&#xff0c; 2. 选择 Apps & Credentials 点击 Create App创建应用&#xff0c;创建后点击编辑按钮&#xff0c;如图&#xff1a; 3. 进入应用详情&#xff0c;勾选Log in with PayPal点击 Advanced Settings 添加return URL等信息并保存。如图&a…