数据结构——堆的实现(详解)

news2024/12/23 9:06:19

呀哈喽,我是结衣。

堆的介绍

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

性质

堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。
在这里插入图片描述
大小堆如同所示。

堆的实现

介绍的话就到此为止,下面我们来进行堆的实现。无非就是那几样。

结构体的创建

typedef int HpDataType;
typedef struct heap
{
	HpDataType* a;
	int size;
	int capacity;
}HP;

是不是很熟悉,没错就是顺序表,但实现起来会比顺序表更难一些哦。

函数的声明

我们先把初始化,销毁,插入,删除等等要实现的函数声明一下:

void HpInit(HP* php);
void Hppush(HP* php, HpDataType x);
void Hpdestroy(HP* php);
void Hppop(HP* php);
bool Hpempty(HP* php);
HpDataType Hptop(HP* php);
int Hpsize(HP* php);

好像没多少函数?

初始化

和顺序表相同。

void HpInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}

销毁

void Hpdestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}

也是一样的说。

插入

这里开始就不一样了,我们要想怎么插才能维持小堆的结构。这里的插入是尾插,是直接插进去就不管了吗?
在这里插入图片描述
如果我们在最后插入的数据是一个大于等于56的数就没有问题,但是如果我们插入的是一个小于56的数呢?直接插入就不会满足小堆的结构了。
在这里插入图片描述
这里我们插入40,40比56小那么它应该是“父亲”,所以我们就要把他和56调换位置,但是如果我们再多想一想,我插入的是9呢,是不是就还要和10调换一下位置了?对的,这就是小堆的内涵,小的永远再上面。下面我们来写一下代码:

void Hppush(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)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size++] = x;
	adjustup(php->a, php->size - 1);
}

中间有个扩容,我们讲过很多次了,就不展开讲了。
我们发现下面我有写了有个函数,向上调整的函数adjustup,没错上面我们讲的谁小于谁交换就要通过这个函数来实现。

向上调整

我们把size-1作为孩子传归来,这个还有一个重要的知识就是**父下标是子下标-1后除以2的结果。**这个用到是整形会自动忽略小数的机制。至于公式怎么来的,我把下标标出来了,你自己推推看就了解了。
在这里插入图片描述

void adjustup(HpDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child>0)
	{
		if (a[child] < a[parent])
		{
			swap(a,child, parent);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
		
	}
}

里面还有一个swap函数,就是交换函数,它的实现在下面。回到adjustup,里面的判断大家应该是没有问题的吧,子与父交换后,我们就让child来到了parent的位置了,但是我们还要继续向上判断,所以parent改变为新child的父下标。
在这里插入图片描述

swap函数

void swap(HpDataType*a,int child, int parent)
{
	HpDataType tmp = a[child];
	a[child] = a[parent];
	a[parent] = tmp;
}

交换就是了

删除

在堆里删除也是一大难点,首先你来猜我们是头删还是尾删?
3
2
1
答案是头删加尾删,单纯尾删在这里没有意义,但这个头删也要用到尾删,你想想如果我们是把头删除了,后面的数再向前覆盖的话,那效率低且不说,而且亲子关系全部乱了。所以绝对不是单纯的头删,我们要把头数据和尾数据交换然后尾删。删除后再向下调整。

void Hppop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	//int child = php->size-1;
	//int parent = (child - 1) / 2;
	swap(php->a, php->size - 1, 0);
	php->size--;
	adjustdown(php->a,0,php->size);
}

向下调整

void adjustdown(HpDataType* a,int parent,int size)
{
	int child = parent * 2 + 1;
	if (a[child] > a[child + 1])
	{
		child++;
	}
	while (child<size)
	{
		if (a[parent] > a[child])
		{
			swap(a, child, parent);
			parent = child;
			child = parent * 2 + 1;
			if (a[child] > a[child + 1])
			{
				child++;
			}
		}
		else
		{
			break;
		}
	}
}

向下调整和向上调整也没太大的区别,都是判断后再交换,你只要清楚循环结束的条件就可以了。最重要的还是判断孩子中较小的数据,这里我们用的是假设法,我们先假设左孩子为小的数据,然后我们在判断如果不对就让孩子++,变成右孩子。

判空

bool Hpempty(HP* php)
{
	assert(php);
	return php->a == NULL;
}

返回顶部数据

HpDataType Hptop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

判断有效数据的个数

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

测试

在这里插入图片描述

代码

heap.h

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int HpDataType;
typedef struct heap
{
	HpDataType* a;
	int size;
	int capacity;
}HP;
void HpInit(HP* php);
void Hppush(HP* php, HpDataType x);
void Hpdestroy(HP* php);
void Hppop(HP* php);
bool Hpempty(HP* php);
HpDataType Hptop(HP* php);
int Hpsize(HP* php);

heap.c

#include "heap.h"
void HpInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}
void Hpdestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}
void swap(HpDataType*a,int child, int parent)
{
	HpDataType tmp = a[child];
	a[child] = a[parent];
	a[parent] = tmp;
}

void adjustup(HpDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child>0)
	{
		if (a[child] < a[parent])
		{
			swap(a,child, parent);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
		
	}
}
void Hppush(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)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size++] = x;
	adjustup(php->a, php->size - 1);
}

void adjustdown(HpDataType* a,int parent,int size)
{
	int child = parent * 2 + 1;
	if (a[child] > a[child + 1])
	{
		child++;
	}
	while (child<size)
	{
		if (a[parent] > a[child])
		{
			swap(a, child, parent);
			parent = child;
			child = parent * 2 + 1;
			if (a[child] > a[child + 1])
			{
				child++;
			}
		}
		else
		{
			break;
		}
	}
}

void Hppop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	//int child = php->size-1;
	//int parent = (child - 1) / 2;
	swap(php->a, php->size - 1, 0);
	php->size--;
	adjustdown(php->a,0,php->size);
}
bool Hpempty(HP* php)
{
	assert(php);
	return php->a == NULL;
}

HpDataType Hptop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

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

test.c

#include "heap.h"

int main()
{
	HP hp;
	HpInit(&hp);
	HpDataType* arr[] = { 21,141,41,1,1,42,9 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz; i++)
	{
		Hppush(&hp, arr[i]);
		
	}
	for (int i = 0; i < hp.size; i++)
	{
		printf("%d ", hp.a[i]);
	}
	printf("\n");
	printf("%d ", hp.a[Hptop(&hp)]);
	printf("\n");
	Hppop(&hp);
	Hppop(&hp);
	Hppop(&hp);
	Hppop(&hp);
	Hppop(&hp);
	Hppop(&hp);
	for (int i = 0; i < hp.size; i++)
	{
		printf("%d ", hp.a[i]);
	}
	return 0;
}


在这里插入图片描述

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

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

相关文章

Linux的基本指令(二)

目录 前言 学前补充 touch指令 mkdir指令 rmdir指令 rm指令 通配符* man指令 cp指令 mv指令(重要) 补充内容&#xff1a; 1、如何快速在Linux中写出代码 2、如何看待如此多的Linux指令 cat指令 前言 关于Linux的基本指令我们会分三到四篇文章进行分析&#xff0c…

【Docker】Docker 仓库管理和Docker Dockerfile

作者简介&#xff1a; 辭七七&#xff0c;目前大二&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

js无法请求后端接口,别的都可以?

在每个接口的控制器中加入以下代码即可&#xff1a; header(Access-Control-Allow-Methods:*); header("Access-Control-Allow-Origin:*"); 如果嫌麻烦可以添加在api初始函数里面

jpom学习

jpom学习 整理jpom 一键安装 部署会需要 mvn跟jdk环境 # 安装服务端和 jdk、maven 环境 yum install -y wget && \ wget -O install.sh https://jpom.top/docs/install.sh && \ bash install.sh Server jdkmvndocker安装 安装docker挂载方式安装 docker …

学习分布式事务Seata看这一篇就够了,建议收藏

一、事务的特性 ACID特性 A&#xff08;Atomic&#xff09;&#xff1a;原子性&#xff0c;构成事务的所有操作&#xff0c;要么都执行完成&#xff0c;要么全部不执行&#xff0c;不可能出现部分成功部分失败的情况。 C&#xff08;Consistency&#xff09;&#xff1a;一致…

个体卫生室电子处方操作流程,私人诊所用什么电子处方系统软件,佳易王诊所电子处方软件配方模板如何设置

个体卫生室电子处方操作流程&#xff0c;私人诊所用什么电子处方系统软件&#xff0c;佳易王诊所电子处方软件配方模板如何设置 1、一般电子处方系统的操作流程为&#xff1a;由医师使用软件开电子处方&#xff0c;打印后核对信息医师签字&#xff0c;然后由药剂师审核单据&am…

电压调整型脉宽调制控制集成电路芯片D7500,工作电压范围7V ~ 40V,输出电流(Max)可达200mA,具有欠压锁定功能

D7500/D7500F SMPS 控制器电路&#xff0c;是一块电压调整型脉宽调制控制集成电路。内部包含5V 基准电压电路、两个误差放大器、触发电路、控制输出电路、脉宽调制比较 器、死区时间比较器及一个 振荡器。该电路可转换频率1kHz至300kHz&#xff0c; 基准电压(Vref)的精确度提…

Multi-modal brain tumor image segmentation based on improved U-net model

THE ARCHITECTURE OF IMPROVED NETWORK MODEL 作者未提供代码

ChatGPT文章批量改写伪原创软件说明文档

大家好&#xff0c;我是淘小白~ 最近有很多朋友咨询&#xff0c;chatGPT文章改写插件和改写软件&#xff0c;这个软件之前已经做出来了&#xff0c;用的朋友不是很多&#xff0c;这几天有不少咨询的&#xff0c;现在把说明文档补一下&#xff0c;(#^.^#) 1、软件语言 Pytho…

LeetCode 1457. 二叉树中的伪回文路径:深度优先搜索(DFS) + 位运算优化

【LetMeFly】1457.二叉树中的伪回文路径&#xff1a;深度优先搜索(DFS) 位运算优化 力扣题目链接&#xff1a;https://leetcode.cn/problems/pseudo-palindromic-paths-in-a-binary-tree/ 给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「…

TikTok 将开源“云中和”边缘加速器

“从某种意义上说&#xff0c;我们正在努力破解云的骨干网&#xff0c;以造福于我们&#xff0c;”TikTok产品管理基础设施经理Vikram Siwach指出&#xff0c;他解释了该公司即将开源的“全球服务加速器”的好处&#xff0c;这是一个可编程的边缘平台&#xff0c;可将应用程序需…

【2023传智杯】第六届传智杯程序设计挑战赛AB组-ABC题解题分析详解【JavaPythonC++解题笔记】

本文仅为第六届传智杯程序设计挑战赛-题目解题分析详解的解题个人笔记,个人解题分析记录。 本文包含:第六届传智杯程序设计挑战赛题目、解题思路分析、解题代码、解题代码详解 文章目录 一.前言二.比赛题目(AB俩组)A题题目B题题目C题题目三.解题代码A题解题思路解题代码【J…

华为ensp:单臂路由

通过单臂路由实现vlan之间的互通 将vlan和trunk配置好&#xff0c;我直接就在r1上演示单臂路由 我们要在r1的e0/0/0上面随便配置个ip&#xff0c;如果你不在接口上配置ip那就无法开启协议 R1 interface e0/0/0 进入真实接口随便配置个ip ip add 192.168.10.1 24 再进入子接…

第十八章 解读pytorch优化器与学习率设置(工具)

简介与解读基本概念 学习率对于模型训练效果来说相当重要。 学习率过低会导致学习速度太慢&#xff0c;学习率过高又容易导致难以收敛。 因此&#xff0c;很多炼丹师都会采用动态调整学习率的方法。刚开始训练时&#xff0c;学习率大一点&#xff0c;以加快学习速度&#xf…

OSG粒子系统与阴影-雨效、雪效模拟(2)

雪效模拟示例 雪效模拟示例的代码如程序清单11-2所示&#xff1a; 1. /* 雪效模拟示例 */ 2. void snow_11_2(const string &strDataFolder) 3. { 4. osg::ref_ptr<osgViewer::Viewer> viewer new osgViewer::Viewer(); 5. osg::ref_ptr<osg::G…

一文教你优惠券样式代码编写,让你的网站脱颖而出!

样式1&#xff1a; 代码实例&#xff1a; <div class"box"><div class"itemBox"><div class"leftBox">全额抵扣</div><div class"rightBotton"><button>立即使用</button></div><…

使用YOLOV8 CLI训练自己的数据集

YOLOV8现在可以直接通过命令行工具运行训练, 推理过程了, 方法如下, 首先安装ultralytics的包: pip install ultralytics接着尝试使用yolov8n来简单做个推理: yolo taskdetect modepredict modelyolov8n.pt conf0.25 sourcesome_picture.jpeg接下来我们使用一个安全防护, 包括…

代码随想录算法训练营第四十五天|139.单词拆分、背包问题总结

LeetCode 139. 单词拆分 题目链接&#xff1a;139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; 这道题使用完全背包来实现&#xff0c;我们首先考虑字符串是否可以由字符串列表组成&#xff0c;因此dp数组大小为n 1 &#xff0c;其意义是&#xff0c;在n个位置时是否能…

2015年全国硕士研究生入学统一考试管理类专业学位联考英语(二)试题

Section I Use of English Directions : Read the following text. Choose the best word(s) for each numbered blank and mark A&#xff0c;B&#xff0c;Cor D on the ANSWER SHEET.( 10 points)   In our contemporary culture&#xff0c;the prospect of communicatin…

最全的软件测试教程(功能、工具、接口、自动化、性能)

一、软件测试功能测试 测试用例编写是软件测试的基本技能&#xff1b;也有很多人认为测试用例是软件测试的核心&#xff1b;软件测试中最重要的是设计和生成有效的测试用例&#xff1b;测试用例是测试工作的指导&#xff0c;是软件测试的必须遵守的准则。 在这我也准备了一份…