【数据结构】什么是堆,如何使用无序数组生成一个堆?

news2025/1/18 18:52:08

文章目录

  • 一、堆的概念及其介绍
  • 二、如何使用无序序列构建一个堆?
  • 三、C语言实现堆的基本操作
    • 结构体创建与销毁
    • 获取堆顶数据与个数及堆的判空
    • 堆的插入与删除
  • 源代码分享


一、堆的概念及其介绍

堆(Heap)是计算机科学中一类特殊的数据结构的统称,堆通常是一个可以被看做一棵完全二叉树的数组对象。如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足: <= 且 <= ( >= 且 >= ) i = 0,1, 2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆满足下列性质:

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

    • 每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆
    • 每个结点的值都小于或等于具左石孩子结点 的值,称为小顶堆。
  • 堆总是一棵完全二叉树。

结构图示

在这里插入图片描述

二、如何使用无序序列构建一个堆?

​ 如果有一组无序的数组,{50,10,90,30,70,40,80,60,20},我们将它抽象为逻辑结构,z这时怎么将无序序列变成一个大堆或者小堆呢?

在这里插入图片描述

向上调整法

​ 从下标为1的位置开始,也就是图中10的位置,依次进行向上调整,每次将更小的换到上面,

题目中有个小问题,如何找到父节点?
在这里插入图片描述

  • 父节点 = (子结点-1)/2
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
typedef int HeapDataType;

void swap(HeapDataType* a, HeapDataType* b)		//交换
{
	HeapDataType temp;
	temp = *a;
	*a = *b;
	*b = temp;
}
void Adjustup(HeapDataType* arr, int child)		//向上调整函数
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void CreateHeap(int* a, int n)			//使用无序数组创建堆
{
	for (int i = 1; i < n; i++)
	{
		Adjustup(a, i);
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}

int main()
{
	int arr[] = { 50,10,90,30,70,40,80,60,20 };
	CreateHeap(arr, sizeof(arr) / sizeof(int));
}

向下调整法(更优)

​ 从非叶节点的最后一个数据的下标开始,每次取出孩子中较大或较小的数(看是大堆还是小堆)向下进行调整,由于每多一层,下层是上层的二倍,这种办法直接可以省略掉最后一层,也可以达到建堆的目的,所以这种办法为更优的办法。

由于需要向下调整,所以这种办法需要找到子节点,我们已经知道父结点的运算了,子结点就是父节点的逆运算。

在这里插入图片描述

  • 子节点:父节点*2+1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
typedef int HeapDataType;

void swap(HeapDataType* a, HeapDataType* b)		//交换
{
	HeapDataType temp;
	temp = *a;
	*a = *b;
	*b = temp;
}
void AdjustDown(HeapDataType* arr, int n, int parent)	//向下调整
{
	assert(arr);
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child<n - 1 && arr[child] > arr[child + 1])
		{
			child = child + 1;
		}
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
		}
		parent = child;
		child = child * 2 + 1;
	}
}
void CreateHeap(int* a, int n)					//使用无序数组创建堆
{
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}

int main()
{
	int arr[] = { 50,10,90,30,70,40,80,60,20 };
	CreateHeap(arr, sizeof(arr) / sizeof(int));
}

三、C语言实现堆的基本操作

结构体创建与销毁

顺序存储方式实现堆采用顺序表进行存储。

typedef int HeapDataType;
typedef struct Heap
{
	HeapDataType* arr;			
	int size;			//当前大小
	int capacity;		//当前容量上限
}Heap;

void HeapDestroy(Heap* ph)
{
	assert(ph);			
	free(ph->arr);
	ph->arr = NULL;
	ph->capacity = 0;
	ph->size = 0;
	free(ph);			//由于顺序表空间是申请堆空间的内存所以需要进行释放
}

获取堆顶数据与个数及堆的判空

HeapDataType HeapTop(Heap* ph) 
{
	assert(ph);
	return ph->arr[0];
}

int HeapSize(Heap* ph)
{
	assert(ph);
	return ph->size;
}

int HeapEmpty(Heap* ph)
{
	assert(ph);
	return ph->size == 0;
}

堆的插入与删除

插入时需要注意空间不足问题,如果空间不足,需要进行二次开辟空间,插入时直接插入到堆尾,然后利用上面写好的向上调整函数。

​ 删除时删掉堆顶数据,将堆底数据拿到堆顶,在进行向下调整,即可保证堆性质不变,依然保持原有的大/小堆。

void HeapPush(Heap* ph, HeapDataType x)
{
	assert(ph);
	if (ph->size == ph->capacity)
	{
		int newcapacity = ph->capacity == 0 ? 5 : ph->capacity * 2;
		HeapDataType* temp = (HeapDataType*)realloc(ph->arr, sizeof(int) * newcapacity);
		if (temp == NULL)
		{
			perror("realloc: error");
			return;
		}
		ph->arr = temp;
		ph->capacity = newcapacity;
	}
	ph->arr[ph->size] = x;
	Adjustup(ph->arr, ph->size - 1);
	ph->size++;
}

void HeapPop(Heap* ph)
{
	assert(ph);
	assert(ph->arr);
	assert(!HeapEmpty(ph));
	swap(&ph->arr[ph->size - 1], &ph->arr[0]);
	ph->size--;
	AdjustDown(ph->arr, ph->size, 0);
}

源代码分享

//heap.h
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>


typedef int HeapDataType;
typedef struct Heap
{
	int* arr;
	int size;
	int capacity;
}Heap;

void HeapCreate(Heap* ph);
void HeapDestroy(Heap* ph);
void swap(HeapDataType* a, HeapDataType* b);
void Adjustup(HeapDataType* arr, int child);
void AdjustDown(HeapDataType* arr, int n, int parent);
void HeapPush(Heap* ph, HeapDataType x);
void HeapPop(Heap* ph);
HeapDataType HeapTop(Heap* ph);
int HeapSize(Heap* ph);
int HeapEmpty(Heap* ph);

//Heap.c
#include "Heap.h"

void HeapCreate(Heap* ph)
{
	assert(ph);
	ph->arr = NULL;
	ph->capacity = 0;
	ph->size = 0;

}

void HeapDestroy(Heap* ph)
{
	assert(ph);
	free(ph->arr);
	ph->arr = NULL;
	ph->capacity = 0;
	ph->size = 0;
	free(ph);
}

void swap(HeapDataType* a, HeapDataType* b)
{
	HeapDataType temp;
	temp = *a;
	*a = *b;
	*b = temp;
}


void Adjustup(HeapDataType* arr, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void AdjustDown(HeapDataType* arr, int n, int parent)
{
	assert(arr);
	int child = parent * 2 + 1;
	while(child<n)
	{
		if (child<n-1&&arr[child] > arr[child + 1])
		{
			child = child + 1;
		}
		if (arr[child] < arr[parent])
		{
			swap(&arr[child], &arr[parent]);
		}
		parent = child;
		child = child * 2 + 1;
	}
}

void HeapPush(Heap* ph, HeapDataType x)
{
	assert(ph);
	if (ph->size == ph->capacity)
	{
		int newcapacity = ph->capacity == 0 ? 5 : ph->capacity * 2;
		HeapDataType* temp = (HeapDataType*)realloc(ph->arr, sizeof(int) * newcapacity);
		if (temp == NULL)
		{
			perror("realloc: error");
			return;
		}
		ph->arr = temp;
		ph->capacity = newcapacity;
	}
	ph->arr[ph->size] = x;
	Adjustup(ph->arr, ph->size - 1);
	ph->size++;
}

void HeapPop(Heap* ph)
{
	assert(ph);
	assert(ph->arr);
	assert(!HeapEmpty(ph));
	swap(&ph->arr[ph->size - 1], &ph->arr[0]);
	ph->size--;
	AdjustDown(ph->arr, ph->size, 0);
}

HeapDataType HeapTop(Heap* ph) 
{
	assert(ph);
	return ph->arr[0];
}

int HeapSize(Heap* ph)
{
	assert(ph);
	return ph->size;
}

int HeapEmpty(Heap* ph)
{
	assert(ph);
	return ph->size == 0;
}

//test.c
void CreateHeap(int* a, int n)			//使用向上调整
{
	for (int i = 1; i < n; i++)
	{
		Adjustup(a, i);
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}
void CreateHeap(int* a, int n)		//使用向下调整
{
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
}
int main()
{
	int arr[] = { 50,10,90,30,70,40,80,60,20 };
	CreateHeap(arr, sizeof(arr) / sizeof(int));
}

img

✨本文收录于数据结构理解与实现

当你喜欢一篇文章时,点赞、收藏和关注是最好的支持方式。如果你喜欢我的文章,请不要吝啬你的支持,点赞👍、收藏⭐和关注都是对我最好的鼓励。感谢你们的支持!

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

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

相关文章

LEETCODE 每日一题 1093.大样本统计

题目描述 前往leetcode1093题 我们对 0 到 255 之间的整数进行采样&#xff0c;并将结果存储在数组 count 中&#xff1a;count[k] 就是整数 k 在样本中出现的次数。 计算以下统计数据: minimum &#xff1a;样本中的最小元素。 maximum &#xff1a;样品中的最大元素。 mean &…

【Log】About these two years

文章目录 第一篇博客&#xff1f;成长收获的季节Future 第一篇博客&#xff1f; 第一次在CSDN发布博客&#xff0c;是当初我学习嵌入式的时候&#xff0c;那个时候我们做了一个大创&#xff0c;其中使用到了激光雷达&#xff0c;然后我就负责激光雷达部分代码的编写和设计&…

AI-新手玩转RKNN

关于RKNN RKNN 是Rockchip npu 平台使用的模型类型&#xff0c;以.rknn后缀结尾的模型文件。Rockchip 提供了完整了模型转换 Python 工具&#xff0c;方便用户将自主研发的算法模型转换成 RKNN 模型&#xff0c;同时 Rockchip 也提供了C/C和Python API 接口。 #mermaid-svg-M7l…

电子器件系列37:SD卡座(Push-Push和Push-Pull)

SD卡座是目前最通用的数据存储卡座、记忆卡座。在各种通讯数码产品、安防产品、带储存类产品等设备上都有所应用。有着性价比高、存储容量大、使用便捷、通用性以及安全性强等特点。自弹式SD卡座的卡槽底部会设有一个小直径、小线径的弹簧或一种切口式弹片。当装入SD卡时&#…

Kafka如何保证数据高可靠

这节课给大家分享一下Kafka的数据高可靠。为什么要讲这个&#xff1f;因为Kafka它本身不是一个金融级别数据可靠的分布式消息系统。 虽然说它存储到某个topic里的数据会先拆分多个partition&#xff0c;这体现了分治的一个思想。每一个partition在最终存储的时候会保存多个副本…

117.【微信小程序】

微信小程序 (一)、微信小程序概括1.微信小程序简介(1).小程序与普通网页开发的区别 2.注册微信小程序账号(1).注册小程序账号(2).获取小程序的AppID 3.安装微信开发者工具(1).微信开发者工具的简介:(2).微信开发者工具的下载 4.创建第一个小程序(1).创建小程序步骤(2).开发者工…

域横向移动-传递攻击atschtasks

横向移动就是拿下对方一台主机后&#xff0c;以拿下的那台主机作为跳板&#xff0c;对内网的其他主机再进行后渗透&#xff0c;拿到其他内网主机的权限的过程。叫做横向移动。横向移动的主要目的就是扩大战果。 传递攻击主要建立在明文和hash值获取基础上进行攻击。 at和scht…

Typora改变字体颜色

方法一&#xff1a;下载AutoHotkey并创建快捷键的方法&#xff08;推荐&#xff09; 第一步&#xff1a;在官网&#xff08;https://www.autohotkey.com/&#xff09;下载 AutoHotkey并傻瓜式安装&#xff0c;安装在任意盘符下均可&#xff1b; 第二步&#xff1a;在安装目录…

STM32F4_I2C(从机EEPROM/MPU-6050)协议详解

目录 1. I2C是什么 2. I2C物理层介绍 3. I2C协议层介绍 3.1 I2C基本读写过程 3.1.1 通讯复合格式 3.2 通讯的起始和停止信号 3.3 数据有效性 3.4 地址及数据方向 3.5 响应 4. STM32的I2C特性及架构 4.1 I2C架构剖析 5. I2C通讯过程 5.1 主发送器 5.2 主接收器 6…

Windows文件自动备份——“使用文件历史记录进行备份”

一、背景 因为有一些重要的文件需要经常改动&#xff0c;同时也有“找到某文件某历史版本”的需求&#xff0c;但考虑到时常手动备份比较麻烦&#xff0c;就想到了使用Windows自带的文件备份功能——“使用文件历史记录进行备份”来帮助进行文件的备份和版本管理。 二、环境 1…

一个测试开发人员在字节的7年,太真实了...

测试这条路是坎坷的&#xff0c;我自己深有体会。 我们的起点低&#xff0c;基础差。 测试这个职位也很尴尬&#xff0c;很少有公司会有针对性的对测试人员做成长规划&#xff0c;也很少有公司会那么重视测试人员&#xff0c; 也许进了像 BATJ 这样的大厂会好一点。 但是大多数…

nacos2.2.1搭建

springboot 3.0集成nacos2.2.1内容可评论区Q我 搭建环境&#xff1a; Java版本&#xff1a;11.0.18 系统&#xff1a;window7 数据库&#xff1a;mysql8.0.29 第一步&#xff0c;下载nacos&#xff0c;下载地址&#xff1a; https://github.com/alibaba/nacos/releases/d…

HACKABLE: III实战演练

文章目录 HACKABLE: III实战演练一、前期准备1、相关信息 二、信息收集1、端口扫描2、访问网站3、查看网站源码4、扫描目录5、访问网址6、查看并下载7、访问网站8、查看文件9、解密10、访问网站11、访问网站12、查看文件13、解密14、访问网站15、访问网站16、下载图片17、隐写1…

CrossOver软件好用吗?最新版22.1.1有哪些优势功能

CrossOver2023是一款系统兼容软件 让您可以在Mac和Linux系统上运行Win应用&#xff0c;不必重启系统&#xff0c;不必使用虚拟机。通过CrossOver&#xff0c; 您可以从dock直接启动Wind应用&#xff0c;与您的Mac和Linux系统功能无缝集成。crossover兼容软件是可以在苹果电脑中…

oracle的基本使用(建表,操作表等)

一、表空间&#xff0c;用户 1、创建表空间 使用system登录 创建表空间 waterboss 为表空间名称datafile 用于设置物理文件名称size 用于设置表空间的初始大小autoextend on 用于设置自动增长&#xff0c;如果存储量超过初始大小&#xff0c;则开始自动扩容next 用于设置每次…

tiechui_lesson14_网络连接请求的拦截

这一节主要学习网络请求的过滤&#xff0c;铁锤大佬讲了些关于IRP的知识。 笔记 先祭出一张灵魂作画&#xff0c;这是用来描述IRP的流转形式的。 通过这幅图着重解释了一下IoSkipCurrentIrpStackLocation(pirp);的过程&#xff0c;就是在流转到当前的IRP请求之后&#xff0c…

Jarvis OJ pwn——level1

checksec&& 运行 ida main函数里的vulnerable_function函数存在溢出 给输入分配的栈空间为0x88 利用思路 ret2shellcode 代码 Author : 白银 Date : 2023-05-27 14:43:28 LastEditors : 白银 LastEditTime : 2023-05-27 16:52:19 FilePath : /pw…

五十行代码教你写一个简单的内存池(二级指针的应用)

固定大小内存的内存池实现 该内存池是一个很简单的内存池&#xff0c;只能申请固定大小的内存&#xff0c;仅供学习 要点&#xff1a; 构造隐式链表二级指针 存储结构 typedef struct mempool_s{int block_size; // 每一块的大虚哎int free_count; // 剩余有多少块是可以…

C#医院LIS系统源码 LIS实验室管理信息系统源码 LIS检验系统源码

1、LIS系统技术框架 &#xff08;1&#xff09;总体框架&#xff1a; SaaS架构的Client/Server应用 服务可伸缩&#xff0c;多服务协同 服务可拆分&#xff0c;功能易扩展 &#xff08;2&#xff09;技术细节&#xff1a; 体系结构&#xff1a;Client/Server架构 客户端…

资深SRE带你看阿里云香港故障

一、故障背景 12月18日阿里云香港Region发生重大故障&#xff0c;多个重要互联网服务受到影响&#xff0c;包括澳门日报、金融管理局、澳门银河、莲花卫视、澳门水泥厂等基础服务&#xff0c;澳觅和MFood等外卖平台&#xff0c;多个区块链交易所也受到影响。详情见官方故障报告…