【数据结构】堆(Heap)

news2025/1/4 15:55:41

文章目录

  • 前言
  • 一、堆
    • 1、 概念
    • 2、性质
    • 3、结构
  • 二、堆的实现
    • 1、算法实现:
      • 向下调整算法
      • 向上调整算法(堆的创建)
      • 堆的插入
      • 堆的删除
      • 堆的排序
    • 2、 代码实现(小堆):
      • 堆的定义
      • 交换
      • 检查容量
      • 向下调整
      • 向上调整
      • 堆的初始化
      • 堆的创建
      • 销毁堆
      • 堆的插入
      • 堆的删除
      • 获取堆顶元素
      • 判空
      • 堆排序
      • 打印堆

前言

基本概念:

1、完全二叉树:若二叉树的深度为h,则除第h层外,其他层的结点全部达到最大值,且第h层的所有结点都集中在左子树。

2、满二叉树:满二叉树是一种特殊的的完全二叉树,所有层的结点都是最大值。

一、堆

1、 概念

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<=K2i+2 ,则称为小堆(或大堆)。

将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

2、性质

  1. 堆中某个节点的值总是不大于或不小于其父节点的值;
  2. 堆总是一棵完全二叉树。

3、结构

在这里插入图片描述

二、堆的实现

1、算法实现:

向下调整算法

堆的向下调整:
(以小堆为例)

  • 先设定根节点为当前节点(通过下标获取,标记为cur),比较左右子树的值,找出更小的值,用child来标记。
  • 比较child和cur的值,如果child比cur小,则不满足小堆的规则,需要进行交换。
  • 如果child比cur大,满足小堆的规则,不需要交换,调整结束。
  • 处理完一个节点之后,从当前的child出发,循环之前的过程。
    向下调整(小堆)示例:
    在这里插入图片描述
    向下调整(大堆)示例:

在这里插入图片描述

向上调整算法(堆的创建)

下面我们给出两个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。

根节点左右子树不是堆,我们怎么调整呢?

这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

堆的向上调整:
(以小堆为例)

  • 先设定倒数的第一个叶子节点为当前节点(通过下标获取,标记为cur),找出他的父亲节点,用parent来标记。
  • 比较parent和cur的值,如果cur比parent小,则不满足小堆的规则,需要进行交换。
  • 如果cur比parent大,满足小堆的规则,不需要交换,调整结束。
  • 处理完一个节点之后,从当前的parent出发,循环之前的过程。
int a[] =    {9,7,8,10,3,6}

向上调整(小堆)示例:
在这里插入图片描述

int a[] =    {1,5,3,8,7,6}

向上调整(大堆)示例:
在这里插入图片描述

堆的插入

将数据插入到数组最后,再进行向上调整。

int a[]={5,10,15,20}
int a[]={5,10,15,20,4}

示例:
在这里插入图片描述

堆的删除

删除堆是删除堆顶的数据。

将堆顶的数据和最后一个数据交换,然后删除数组最后一个数据,再进行向下调整算法。

示例:
在这里插入图片描述

堆的排序

基本思想:

将待排序序列构造成一个大顶堆
此时,整个序列的最大值就是堆顶的根节点。
将其与末尾元素进行交换,此时末尾就为最大值。
然后将剩余n-1个元素重新构造成一个堆,这样会得到n-1个元素的次小值。如此反复执行,便能得到一个有序序列了。

动态演示:
可视化网站:数据结构和算法动态可视化

在这里插入图片描述

2、 代码实现(小堆):

(以小堆为示例)

堆的定义

typedef int HDataType;
typedef struct heap
{
	HDataType* data;
	int size;
	int capacity;
}heap;

交换

void Swap(int* a, int* b)
{
	int temp = *a;
	*a = *b;
	*b = temp;
}

检查容量

void checkCapcity(heap* hp)
{
	if (hp->capacity == hp->size)
	{
		int newC = hp->capacity == 0 ? 1 : 2 * hp->capacity;
		hp->data = (HDataType*)realloc(hp->data, sizeof(HDataType)*newC);
		hp->capacity = newC;
	}
}

向下调整

void shiftDown(int* arr, int n, int cur)
{
	int child = 2 * cur + 1;
	while (child < n)
	{
		//比较左右子树,找到较小值
		if (child + 1 < n &&arr[child + 1]<arr[child])
		{	
			++child;
			//child时刻保存较小值的下标
		}
		if (arr[child] < arr[cur])
		{
			Swap(&arr[child], &arr[cur]);
			cur = child;
			child = 2 * cur + 1;
		}
		else
		{
			break;
		}
	}
}

向上调整

void shiftUp(int* arr, int n, int cur)
{
	int parent = (cur - 1) / 2;
	while (cur > 0)
	{
		if (arr[cur] < arr[parent])
		{
			Swap(&arr[cur], &arr[parent]);
			cur = parent;
			parent = (cur - 1) / 2;
		}
		else
		{
			break;
		}
	}

}

堆的初始化

void heapInit(heap* hp)
{
	assert(hp);
	hp->data = NULL;
	hp->capacity = hp->size = 0;
}

堆的创建

void heapCreate(heap* hp, int* arr, int n)
{
	assert(hp);
	hp->data = (HDataType*)malloc(sizeof(HDataType)*n);
	memcpy(hp->data, arr, sizeof(HDataType)*n);
	hp->capacity = hp->size = n;
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		shiftDown(hp->data, hp->size, i);
	}
}

销毁堆

void heapDestory(heap* hp)
{
	assert(hp);
	free(hp->data);
	hp->data = NULL;
	hp->capacity = hp->size = 0;
}

堆的插入

void heapPush(heap* hp, HDataType val)
{
	assert(hp);
	checkCapcity(hp);
	hp->data[hp->size++] = val;
	shiftUp(hp->data, hp->size, hp->size - 1);
}

堆的删除

void heapPop(heap* hp)
{
	if (hp->size > 0)
	{
		swap(&hp->data[0], &hp->data[hp->size - 1]);
		hp->size--;
		shiftDown(hp->data, hp->size, 0);
	}
}

获取堆顶元素

HDataType heapTop(heap* hp)
{
	assert(hp);
	return hp->data[0];
}

判空

int heapEmpty(heap* hp)
{
	if (hp == NULL || hp->size == 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

堆排序

void heapSort(heap* hp)
{
	assert(hp);
	for (int i = (hp->size - 2) / 2; i >= 0; i--)
	{
		shiftDown(hp->data, hp->size, i);
	}
	int end = hp->size - 1;
	while (end > 0)
	{
		Swap(&hp->data[0], &hp->data[end]);
		shiftDown(hp->data, end, 0); 
			end--;
	}
}

打印堆

void HeapPrint(heap* hp)
{
	assert(hp);
	for (int i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->data[i]);
	}
	printf("\n");
}

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

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

相关文章

Linux下C/C++实现DNS查询(DNS QUERY)

DNS 的全称是 Domain Name System 或者 Domain Name Service&#xff0c;它主要的作用就是将人们所熟悉的网址 (域名) “翻译”成电脑可以理解的 IP 地址&#xff0c;这个过程叫做 DNS 域名解析。域名是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称&am…

Linux维护安全-PHP安全-php高危函数禁用-php必需禁用的最高危函数总结-必禁

Linux维护安全-PHP安全-php高危函数禁用-php必需禁用的最高危函数总结&#xff0c;优雅草松鼠蜻蜓系统安装必须开启proc_open,putenv&#xff0c;但是希望大家在安装完成后一定要禁用。 必须禁用的高危函数&#xff1a; system,exec,passthru,shell_exec,popen,proc_open,pute…

国考省考行测:数量关系,十字交叉法,增长率反应去年的量,浓度反应的是浓液,平均反应的是人数

国考省考行测&#xff1a;十字交叉法 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡申论和行测的重要知识点 遇…

3网络互联-3.5【实验】【计算机网络】

3网络互联-3.5【实验】【计算机网络】 前言推荐3网络互联3.4 IP分组转发与静态路由3.5 动态路由配置及RIP协议分析实验目的实验内容及实验环境实验原理1. RIP协议2.RIP分组格式3.RIP协议工作原理 实验过程1&#xff0e;搭建一个多跳网络拓扑&#xff0c;配置RIP协议参数2&#…

【ONE·C++ || 哈希(一)】

总言 主要介绍哈希基本框架及其unordered系列容器简述。 文章目录 总言0、思维导图1、unordered系列介绍2、底层&#xff1a;哈希2.1、哈希概念介绍&#xff1a;哈希(散列)函数和哈希表(散列表)2.2、映射关系建立与问题说明&#xff1a;除留余数法、哈希冲突2.3、闭散列及其实…

企业邮箱解析:为何它是企业必备的高效沟通工具?

企业家都知道&#xff0c;一个一致的品牌可以提高它在人们心中的可靠性。这就是为什么大多数企业投资于他们的网络和企业邮箱提供商的原因&#xff0c;可以体现品牌正规性。 企业邮箱为您提供与您的域名匹配的企业电子邮件(nameyourbusiness.com)。它还为您提供&#xff1a; 1、…

【HelloWorld篇】深入学习Spring-Boot

前言 该专栏用来记录一下深入学习SpringBoot&#xff0c;了解SpringBoot如何扩展、如何自定义编写属于自己的start&#xff0c;了解 AOP&#xff0c;IOC&#xff0c;过滤器&#xff0c;拦截器&#xff0c;注解&#xff0c;定时器等实际开发用到的场景以及如何整合第三方&#…

Vivado综合属性系列之六 MAX_FANOUT

目录 一、前言 二、MAX_FANOUT ​ ​2.1 工程代码 ​ ​2.2 工程结果 ​ 一、前言 ​ ​在时序违例的工程中&#xff0c;有一个很常见的原因&#xff1a;高扇出&#xff0c;此时就需要降低信号的扇出&#xff0c;可通过属性MAX_FANOUT来控制信号的扇出值&#xff…

32.Git分布式版本控制

目录 一、Git分布式版本控制。 &#xff08;1&#xff09;Git简介。 &#xff08;2&#xff09;Git工作流程图。 &#xff08;3&#xff09;Git安装。 &#xff08;4&#xff09;Git基本配置。 &#xff08;4.1&#xff09;用户信息配置&#xff08;不一定是真实的信息&a…

RK3568平台开发系列讲解(项目篇)手写数字识别

🚀返回专栏总目录 文章目录 一、安装飞桨(PaddlePaddle)二、手写数字识别任务2.1、准备训练集和测试集2.2、模型组网2.3、模型训练与评估2.4、模型保存和导出ONNX模型2.5、完整程序2.6、模拟推理和导出RKNN模型三、板端部署推理3.1、安装RKNN Toolkit Lite2和相关库3.2、推…

从零开始学习JVM(五)-运行时数据区的方法区

1. 方法区基本介绍 官方文档&#xff1a;The Java Virtual Machine Specification 《Java虚拟机规范》中明确说明&#xff1a;“尽管所有的方法区在逻辑上是属于堆的一部分&#xff0c;但一些简单的实现可能不会选择去进行垃圾收集或者进行压缩。”但对于HotSpotJVM而言&…

手写简易RPC框架

目录 简介 服务提供者 服务注册&#xff1a;注册中心 HttpServerHandler处理远程调用请求 consumer服务消费端 简介 RPC&#xff08;Remote Procedure Call&#xff09;——远程过程调用&#xff0c;它是一种通过网络从远程计算机程序上请求服务&#xff0c; 而不需要了解…

我在VScode学Java(Java二维数组)

我的个人博客主页&#xff1a;如果\真能转义1️⃣说1️⃣的博客主页 关于Java基本语法学习---->可以参考我的这篇博客&#xff1a;(我在Vscode学Java) 接上回Java一维数组的介绍&#xff08;我在VScode学Java(Java一维数组&#xff09; &#xff09; 二维数组是Java中的一…

MySQL:数据库的约束

目录 1.数据库约束 1.1 非空&#xff1a;not null 1.2 唯一&#xff1a;unique ​​​​​​​ 1.3 默认值&#xff1a;default 1.4 列描述&#xff1a;comment 1.5 主键约束&#xff1a;primary key 1.6 外键约束 1.7 综合案例 2.插入查询结果 3.聚合函数 4.group by…

AI心理咨询师:舒缓焦虑,解放压力的秘诀

在如今高压力的生活和工作环境下&#xff0c;焦虑和内耗成为了越来越多人的问题。这一现象对我们的身体和心理都会造成很大的影响。如何治愈我们的焦虑和精神内耗&#xff1f; 1.减少工作压力 - 了解和认可自己的能力和限制&#xff0c;不要让工作压力压垮自己。 - 适当的规…

软件测试工程师的职业发展方向

一、软件测试工程师大致有4个发展方向: 1 资深软件测试工程师 一般情况&#xff0c;软件测试工程师可分为测试工程师、高级测试工程师和资深测试工程师三个等级。 达到这个水平比较困难&#xff0c;这需要了解很多知识&#xff0c;例如C语言&#xff0c;JAVA语言&#xff0c…

浙大数据结构

题目详情&#xff1a;06-图1 列出连通集 给定一个有N个顶点和E条边的无向图&#xff0c;请用DFS和BFS分别列出其所有的连通集。假设顶点从0到N−1编号。进行搜索时&#xff0c;假设我们总是从编号最小的顶点出发&#xff0c;按编号递增的顺序访问邻接点。 输入格式: 输入第1…

<数据结构>NO5.栈和队列

目录 栈 Ⅰ.栈的概念 Ⅱ.栈的实现 Ⅲ.测试代码 队列 Ⅰ.队列的概念 Ⅱ.队列的实现 &#x1f4ad;前言 栈和队列也是一种常见的线性存储的数据结构&#xff0c;只不过它们的某些操作受到了限制&#xff0c;比如栈只允许从栈顶插入删除元素、队列只允许从队尾插入元素&…

[算法] ArrayList 和 LinkedList 的优缺点比较及使用场景

&#x1f61a;一个不甘平凡的普通人&#xff0c;致力于为Golang社区和算法学习做出贡献&#xff0c;期待您的关注和认可&#xff0c;陪您一起学习打卡&#xff01;&#xff01;&#xff01;&#x1f618;&#x1f618;&#x1f618; &#x1f917;专栏&#xff1a;算法学习 &am…

MySQL高级_第12章_数据库其它调优策略

MySQL高级_第12章_数据库其它调优策略 1. 数据库调优的措施 1.1 调优的目标 尽可能 节省系统资源 &#xff0c;以便系统可以提供更大负荷的服务。&#xff08;吞吐量更大&#xff09; 合理的结构设计和参数调整&#xff0c;以提高用户操作 响应的速度 。&#xff08;响应速…