【数据结构】堆(超详细)

news2024/12/26 23:36:01

文章目录

  • 前言
  • 堆的概念及结构
  • 堆的实现
    • 堆的向下调整算法(建小堆为例)
    • 堆的向上调整算法(建小堆为例)
    • 堆的初始化
    • 销毁堆
    • 堆的插入
    • 堆的删除(规定删堆顶的数据)
    • 取堆顶元素
    • 判断堆是否为空
    • 获取堆的个数
  • 完整代码(包括测试代码)
    • Heap.h
    • Heap.c
    • test.c

前言

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。在现实中我们通常把堆 (一种完全二叉树) 使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

堆的概念及结构

在这里插入图片描述

【大根堆和小根堆】:

根结点最大的堆叫做大根堆,树中所有父亲都大于或等于孩子。

根结点最小的堆叫做小根堆,树中所有父亲都小于或等于孩子。
在这里插入图片描述

堆的实现

首先新建一个工程:

Heap.h(堆的类型定义、接口函数声明、引用的头文件)
Heap.c(堆接口函数的实现)
test.c(主函数、测试栈各个接口功能)

完整的代码放在后面(包括测试代码),这里就不会展示测试的效果图。大家可以自己别敲边按测试代码测试。图解会写的很详细的,么么😙

堆的向下调整算法(建小堆为例)

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。
向下调整算法有一个前提:左右子树必须是一个堆,才能调整
int arr[] = {27,15,19,18,28,34,65,49,25,37};
在这里插入图片描述

思想:
1.选出左右孩子较小的一个值,
2.然后和父亲进行比较,如果比父亲小就进行交换,并将原来小的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。如果比父亲大,则停止向下调整,此时该树就成小堆。

//交换函数
void Swap(HDataType* p1, HDataType* p2)
{
	HDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;

}

//向下调整算法
void AdjustDown(HDataType* 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 = child * 2 + 1;
		}
		else // 如果父亲小于孩子,说明已经为小堆了,停止调整
		{
			break;
		}
	}

}

堆的向上调整算法(建小堆为例)

当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。
假设在这个堆int arr[] = {15,18,19,25,28,34,65,49,27,37}的末尾插入16.
在这里插入图片描述

思想:
1.将要插入孩子的值与父亲的值比较。
2.若插入孩子的值比父亲的值小,则交换插入孩子的值与父亲的值,并将父亲的位置当作新的插入孩子的值继续进行向上调整。若插入孩子的值比父亲的值大,则停止向上调整,此时该树就成小堆。


//交换函数
void Swap(HDataType* p1, HDataType* p2)
{
	HDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;

}

//向上调整算法
void AdjustUp(HDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

堆的初始化

//初始化堆
void HPInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}

销毁堆

//销毁堆
void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}

堆的插入

数据插入时是插入到数组的末尾,即树形结构的最后一层的最后一个结点,所以插入数据后我们需要运用堆的向上调整算法对堆进行调整,使其在插入数据后仍然保持堆的结构。

//堆的插入
void HPPush(HP* php, HDataType x)
{
	assert(php);
	//检查空间是否足够插入,不够则扩容
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HDataType* tmp = (HDataType*)realloc(php->a, sizeof(HDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	//插入元素
	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);// 从插入的元素开始,进行向上调整,保持它依然是堆
}

堆的删除(规定删堆顶的数据)

堆的删除,删除的是堆顶的元素,但是这个删除过程可并不是直接删除堆顶的数据,
1.将堆顶的数据与最后一个结点的数据交换,
2.删除堆种最后一个元素,此时左子树和右子树还是小堆
3.再对堆进行向下调整

//堆的删除(规定删堆顶的数据)
void HPPop(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);// 从根节点开始,对剩下元素进行向下调整成大堆,保持它依然是堆
}

取堆顶元素

//取堆顶元素
HDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);//确保堆里有数据
	return php->a[0];//返回堆顶数据
}

判断堆是否为空

//判断堆是否为空
bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;//判断堆中数据是否为0
}

获取堆的个数

//获取堆的个数
int HPSize(HP* php)
{
	assert(php);
	return php->size;//返回堆中数据个数
}

完整代码(包括测试代码)

Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>


typedef  int HDataType;
typedef struct Heap
{
	HDataType* a;
	int size;
	int capacity;

}HP;

//交换函数
void Swap(HDataType* p1, HDataType* p2);
//向上调整算法
void AdjustUp(HDataType* a, int child);
//向下调整算法
void AdjustDown(HDataType* a, int size, int parent);



//初始化堆
void HPInit(HP* php);
//销毁堆
void PHDestroy(HP* php);
//堆的插入
void HPPush(HP* php, HDataType x);
//堆的删除(规定删堆顶的数据)
void HPPop(HP* php);
//取堆顶元素
HDataType HPTop(HP* php);
//判断堆是否为空
bool HPEmpty(HP* php);
//获取堆的个数
int HPSize(HP* php);



Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"


//小堆
void Swap(HDataType* p1, HDataType* p2)
{
	HDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;

}

//向下调整算法
void AdjustDown(HDataType* 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 = child * 2 + 1;
		}
		else
		{
			break;
		}
	}

}
//向上调整算法
void AdjustUp(HDataType* a, int child)//可以用递归写,没必要,用循环就可以
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}


	}

}
//初始化堆
void HPInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}

//销毁堆
void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;

}

//堆的插入
void HPPush(HP* php, HDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HDataType* tmp = (HDataType*)realloc(php->a, sizeof(HDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size] = x;
	php->size++;

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

//堆的删除(规定删堆顶的数据)
void HPPop(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);
}


//取堆顶元素
HDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

//判断堆是否为空
bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}
//获取堆的个数
int HPSize(HP* php)
{
	assert(php);
	return php->size;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"


int main()
{
	int a[] = { 4,3,7,9,1,5,8,2,8 };
	int sz = sizeof(a) / sizeof(a[0]);
	HP hp;
	HPInit(&hp);
	for (int i = 0; i < sz; i++)
	{
		HPPush(&hp, a[i]);
	}
	//int k = 5;
	//while (k--)
	//{
	//	printf("%d\n", HPTop(&hp));
	//	HPPop(&hp);
	//}
	while (!HPEmpty(&hp))
	{
		printf("%d ", HPTop(&hp));
		HPPop(&hp);
	}
	printf("\n");

	return 0;
}

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

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

相关文章

解决kali Linux2024无法获取动态IPv4地址(DHCP)解决方案

用root用户启动终端 进入根目录&#xff0c;选择配置文件 cd到根目录下/../etc/network找到interfaces文件 编辑interfaces文件 vi interfaces&#xff0c;编辑interfaces文件 输入如下命令 打开虚拟网络编辑器 选择虚拟机选项卡&#xff0c;编辑&#xff0c;打开虚拟网络编…

【Linux 网络】网络基础(二)(应用层协议:HTTP、HTTPS)-- 详解

我们程序员写的一个个解决我们实际问题&#xff0c;满足我们日常需求的网络程序&#xff0c;都是在应用层。 前面写的套接字接口都是传输层经过对 UDP 和 TCP 数据发送能力的包装&#xff0c;以文件的形式呈现给我们&#xff0c;让我们可以进行应用层编程。换而言之&#xff0c…

50.乐理基础-拍号的类型-混合拍子

混合拍子的定义&#xff1a; 1.由不同的单拍子组合起来的&#xff0c;如图1。 2.因为组合顺序有多种可能&#xff0c;所以次强拍的位置也有多种可能&#xff0c;如图3。 图1&#xff1a;四二拍是单拍子&#xff0c;四三拍也是单拍子&#xff0c;四二拍 与 四三拍就是 不同的单拍…

网络游戏同步技术六:若干问题探讨

本文探讨网络同步的几个问题。 王者荣耀使用帧同步明智吗&#xff1f; 这个很难评&#xff0c;成王败寇&#xff0c;它成功了&#xff0c;它就是明智的。但是帧同步带来的心智负担还是很重的&#xff0c;他们的分享里面也提到他们花了很大的功夫去解决不一致问题。 个人更喜欢…

Sketch总结

sketch禁用了lineGap https://www.sketch.com/docs/designing/text/ http://www.sketchcn.com/sketch-chinese-user-manual.html https://github.com/sketch-hq/sketch-document https://developer.sketch.com/file-format/ https://animaapp.github.io/sketch-web-viewer/ htt…

定时发圈操作介绍

1、登陆已有的账号&#xff0c;点击到"朋友圈"功能 2、选择要发圈的微信号&#xff0c;编辑发圈的文案内容 3、自定义想要的时间点 4、点击"立即发送" 5、可进行跟圈

校园防欺凌平台

校园霸凌事件很难被发现&#xff0c;发现者又可能迫于威胁而不敢告发&#xff0c;被霸凌者又因各种原因而选择忍耐&#xff0c;所以&#xff0c;如果能够在发生校园霸凌的时候&#xff0c;做出及时的预警&#xff0c;也许能够拯救挽回无数个家庭。本平台结合防欺凌设备&#xf…

Transformer - Self-Attention层的复杂度的计算

Transformer - Self-Attention层的复杂度的计算 flyfish 矩阵的维度 下面矩阵的维度是32即 3行&#xff0c;2列 6,10等都是矩阵里的元素 如果矩阵A的列数与矩阵B的行数相同&#xff0c;那么这两个矩阵可以相乘。即&#xff0c;若A是一个mn矩阵&#xff0c;B是一个np矩阵&am…

python文件操作常用方法(读写txt、xlsx、CSV、和json文件)

引言 用代码做分析的时候经常需要保存中间成果&#xff0c;保存文件格式各不相同&#xff0c;在这里好好总结一下关于python文件操作的方法和注意事项 Python 提供了丰富的文件操作功能&#xff0c;允许我们创建、读取、更新和删除文件。允许程序与外部世界进行交互。 文章目录…

用c++实现快速排序、最大子段和问题

6.2.2 快速排序 【问题】快速排序(quick sort)的分治策略如下&#xff08;图6-5)。 (1)划分&#xff1a;&#xff08;选定一个记录作为轴值&#xff0c;以轴值为基准将整个序列划分为两个子序列&#xff0c;轴值的位置在划分的过程中确定&#xff0c;并且左侧子序列的所有记录…

mysql主从热备+keepalived 部署mysql高可用主备模式

目录 1、环境准备 2、分别在主服务器和备用服务器上安装keepalived 3、修改keepalived服务的配置文件 3.1 修改主服务器上的keepalive服务的配置文件 3.2 修改备用服务器上的keepalive服务配置文件 4、编写mysql监控脚本放到主服务器上 5、在主服务器和备用服务器上查看…

工厂自动化升级改造(3)-Modbus与MQTT的转换

什么是MQTT,Modbus,见下面文章 工厂自动化升级改造参考(01)--设备通信协议详解及选型-CSDN博客文章浏览阅读608次,点赞9次,收藏6次。>>特点:基于标准的以太网技术,使用TCP/IP协议栈,支持高速数据传输和局域网内的设备通信。>>>特点:跨平台的通信协议,…

【Java基础】集合(2) —— List

List 存储的对象是有序的&#xff08;集合中存储对象的顺序和使用add方法添加对象的顺序一致&#xff09;&#xff0c;存储的对象是可重复的。 List的特有的功能: 都是可以操作索引的功能。 增: void add(int index, E element )boolean addAll(int index, Collection<? …

全面提升数据采集效率:IP代理产品的应用与评估详解

全面提升数据采集效率&#xff1a;IP代理产品的应用与评估详解 文章目录 全面提升数据采集效率&#xff1a;IP代理产品的应用与评估详解背景应用场景&#xff1a;平台首页信息抓取准备评测素材详细的产品使用和评测流程产品介绍亮数据的IP代理服务亮数据的爬虫工具及采集技术 注…

MFC通过继承现有控件自定义控件

在MFC 自定义控件&#xff0c;可以通过继承MFC提供的控件类&#xff08;如CButton、CEdit、CListBox等&#xff09;并重写其成员函数和消息处理函数来实现。 以下是一个基本的步骤指南&#xff0c;用于在MFC中创建自定义控件&#xff1a; 确定要继承的基类&#xff1a; 首先…

AutoNeRF:Training Implicit Scene Representations with Autonomous Agents

论文概述 《AutoNeRF》是由Pierre Marza等人撰写的一篇研究论文&#xff0c;旨在通过自主智能体收集数据来训练隐式场景表示&#xff08;如神经辐射场&#xff0c;NeRF&#xff09;。传统的NeRF训练通常需要人为的数据收集&#xff0c;而AutoNeRF则提出了一种使用自主智能体高效…

大模型来了,创业者怎么做出好产品?

大模型的问世惊艳了人们的目光&#xff0c;打开了对AI想象力——生成未来&#xff0c;是谁的未来&#xff1f; “电的发明并不是只能让爱迪生的公司成为全球最大公司&#xff0c;而是为众多电器制造商也提供了巨大的商机。从人类科技史的角度来看&#xff0c;应用层面的价值往…

C++列表实现

文章目录 一、listView相关内容主要思想实例全部代码 二、QTreeView 一、listView 相关内容 QAbstractItemModel&#xff1a;一个抽象的类&#xff0c;为数据项模型提供抽象的接口&#xff0c;常见的的数据模型列如&#xff1a;QStringListModel,QStandardItemMode,QDirModel…

第一个fyne应用

第一个fyne应用 由于在写一个milvus的图形化工具&#xff0c;方便客户端使用&#xff0c;调研了一下只有这fyne的go-gui的star最多&#xff0c;比较流行&#xff0c;因此打算使用这个框架来进行milvus的工具开发。 第一个fyne应用 依赖go.mod: module fynedemogo 1.20requi…

BGP学习三:BGP路由优选12条规则,闪亮登场啦啦啦啦啦

目录 一.BGP策略工具 &#xff08;1&#xff09;Router-policy作用 &#xff08;2&#xff09;组成部分 &#xff08;3&#xff09;router-policy注意事项 二.优选规则 ①丢弃下一跳不可达 (1)优选prefered-value值大的路由 1.首选优先级 (2)优选local-preference(本地…