堆 (带图详解)

news2025/7/16 19:48:41

文章目录

  • 1.堆的基本概念
    • 1. 概念
    • 2.性质
      • 1.必须为完全二叉树
      • 2.满足大堆/小堆成立的条件
    • 3.存储方式
      • 1.逻辑结构
      • 2.物理结构
    • 4. 孩子与父亲之间下标的关系
  • 2.堆的基本实现
    • 1.push——插入
      • 1.代码
      • 2. 情况分析
        • 情况1
        • 情况2
      • 3. 向上调整算法
        • 1.过程分析
        • 2. 临界条件的判断
    • 2. pop—— 删除
      • 1.代码
      • 2. 向下调整算法
        • 1. 注意事项
        • 2. 临界条件
        • 3.TOPK问题
          • 1.过程分析
    • 3. create ——建堆
      • 1.向下调整算法的应用
    • 4. top—— 取堆顶元素
    • 5. size—— 数据个数
    • 6. empty ——判空
    • 7. TOPK问题的具体实现
  • 完整代码
      • 1.heap.h
      • 2.heap.c
      • 3.test.c

1.堆的基本概念

1. 概念

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

2.性质

1.必须为完全二叉树

在这里插入图片描述

2.满足大堆/小堆成立的条件

在这里插入图片描述

  • 大堆:树中所有父亲节点都大于等于孩子节点

在这里插入图片描述

  • 小堆:树中所有父亲节点都小于等于孩子节点

3.存储方式

1.逻辑结构

在这里插入图片描述

  • 逻辑结构:我们想象出来的

2.物理结构

在这里插入图片描述

  • 物理结构:实实在在在内存是如何存储的

4. 孩子与父亲之间下标的关系

在这里插入图片描述

  • leftchild=parent*2+1

  • rightchild=parent*2+2

  • parent=(child-1)/2

这里child 可以是leftchild,也可以是rightchild,因为整数相除得到的结果也为整数

2.堆的基本实现

1.push——插入

1.代码

void adjustup(HPDatatype* a, int child)//向上调整算法
{
	int parent = (child - 1) / 2;
		while (child>0)
	{
		if (a[parent] >a[child])//以小堆为例
		{
		swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
        }
		else
		{
			break;
		}
	 }
}
void heappush(HP* php, HPDatatype x)//插入
{
	assert(php);
	if (php->capacity == php->size)//扩容
	{
		HPDatatype* ptr = (HPDatatype*)realloc(php->a, sizeof(HPDatatype)*php->capacity * 2);
		if (ptr != NULL)
		{
			php->a = ptr;
			php->capacity *= 2;
		}
	}
	php->a[php->size++] = x;//插入数据
	adjustup(php->a,php->size-1);  //向上调整

}

2. 情况分析

在这里插入图片描述

由图可知目前是一个小堆

情况1

  • 在数组后插入 >=56 的数 例如 100
    在这里插入图片描述

此时依旧为一个小堆,不需要调整,直接插入在数组尾部就可以了

情况2

  • 在数组后插入<56的数 例如 22
    在这里插入图片描述
  • 在圈中22比56小,所以不构成小堆,需要进行向上调整

3. 向上调整算法

1.过程分析

  • 这里以小堆为例

在这里插入图片描述

  • 我们要创建小堆,parent(56)>child(22),所以要将两者值进行交换

在这里插入图片描述

  • 假设我们并不知道上面的数 例如10 与 新交换后的parent 22 的关系 所以我们需要向上调整

在这里插入图片描述

  • 即将 parent的下标赋给 child ,即22成为新的child下标对应位置,10成为parent下标对应位置 ,此时因为10<22,所以走break不需要调整

2. 临界条件的判断

在这里插入图片描述

  • 当child下标处于0时,parent下标已经越界没有比较的必要了,所以child>0
    就为临界条件

2. pop—— 删除

1.代码

void adjustdown(HPDatatype* a, int parent,int size)//向下调整算法
{
	int child = parent * 2 + 1;//假设为左孩子
	while (child<size)
	{
		if (child+1<size&&a[child] < a[child + 1])//如果假设不成立,就为右孩子
		{
			child++;
		}
		if (a[parent] < a[child])//孩子大于父亲
		{
			swap(&a[parent], &a[child]);
			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,0,php->size);//向下调整算法
}

2. 向下调整算法

1. 注意事项

在这里插入图片描述

在这里插入图片描述

若右孩子所对应没有结点,a[child+1]就会越界访问,
所以需要加上限制条件: child+1<size

2. 临界条件

在这里插入图片描述

  • child作为下标存在,n为数据个数,child最多等于n-1

3.TOPK问题

  • N个数,找最大/最小的前k个

这里我们以大堆来举例 寻找最大的前k个
在这里插入图片描述

1.过程分析

在这里插入图片描述

  • 刚开始时,我们需要将首尾互换,并将此时的尾数据删除

在这里插入图片描述

  • 交换parent下标与child下标所对应的值,如图1 2
  • 并将child的下标赋给parent 如 图 2 3

3. create ——建堆

void heapcreate(HP* php, HPDatatype* a, int n)//建堆
{
	assert(php);
	php->a = (HPDatatype*)malloc(sizeof(HPDatatype) * n);
	if (php->a == NULL)
	{
		perror("mealloc fail");
		exit(-1);
	}
	memcpy(php->a, a,  sizeof(HPDatatype) * n);
	int i = 0;
	for (i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		adjustdown(php->a, i, n);
	}

}

1.向下调整算法的应用

在这里插入图片描述

我们想创建一个大堆,就必须使左子树是一个大堆及右子树是一个大堆
所以要从倒数第二层开始调整
在这里插入图片描述

4. top—— 取堆顶元素

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

5. size—— 数据个数

int heapsize(HP* php)//数据个数
{
	assert(php);
	assert(php->size > 0);
	return php->size;
}

6. empty ——判空

bool heapempty(HP* php)//判断是否为空
{
	assert(php);
	assert(php->size > 0);
	return php->size == 0;
}

7. TOPK问题的具体实现

#include "heap.h"
int main()
{
	HP php;
	heapinit(&php);
	int arr[] = {27,15,19,18,28,34,65,49,25,37};
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		heappush(&php, arr[i]);
	}
	print(&php);
	int k = 5;//取前5个数
	while (k--)
	{
		printf("%d ", heaptop(&php));
		heappop(&php);
	}
	heapdestroy(&php);
	return 0;
}

在这里插入图片描述

完整代码

1.heap.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int  HPDatatype;
typedef struct Heap
{
	HPDatatype* a;
	int size;
	int capacity;
}HP;
void heapcreate(HP* php, HPDatatype a, int size);
void heapinit(HP* php);//初始化
void heapdestroy(HP* php);//内存销毁
void heappush(HP* php,HPDatatype x);//插入
void heappop(HP* php);//删除
HPDatatype heaptop(HP* php);//堆顶数据
int heapsize(HP* php);//数据个数
bool heapempty(HP* php);//判断是否为空


2.heap.c

#include"heap.h"
//void heapcreate(HP* php, HPDatatype* a, int n)//建堆
//{
//	assert(php);
//	php->a = (HPDatatype*)malloc(sizeof(HPDatatype) * n);
//	if (php->a == NULL)
//	{
//		perror("mealloc fail");
//		exit(-1);
//	}
//	memcpy(php->a, a,  sizeof(HPDatatype) * n);
//	int i = 0;
//	for (i = (n - 1 - 1) / 2; i >= 0; i--)
//	{
//		adjustdown(php->a, i, n);
//	}
//
//}
void  heapinit(HP* php)//初始化
{
	assert(php);
	php->a = (HP*)malloc(sizeof(php) *4);
	php->size = 0;
	php->capacity = 4;
}

void heapdestroy(HP* php)//内存销毁
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->size = 0;
	php->capacity = 0;
}
void swap(HPDatatype* s1, HPDatatype* s2)
{
	int tmp = 0;
	tmp = *s1;
	*s1 = *s2;
	*s2 = tmp;
}
//void adjustup(HPDatatype* a, int child)//向上调整算法
//{
//	int parent = (child - 1) / 2;
//	while (child>0)
//	{
//		if (a[parent] >a[child])//以小堆为例
//		{
//			swap(&a[parent], &a[child]);
//			child = parent;
//			parent = (child - 1) / 2;
//	    }
//		else
//		{
//			break;
//		}
//	}
//}
void adjustup(HPDatatype* a, int child)//向上调整算法
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] < a[child])//以大堆为例
		{
			swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

void heappush(HP* php, HPDatatype x)//插入
{
	assert(php);
	if (php->capacity == php->size)//扩容
	{
		HPDatatype* ptr = (HPDatatype*)realloc(php->a, sizeof(HPDatatype)*php->capacity * 2);
		if (ptr != NULL)
		{
			php->a = ptr;
			php->capacity *= 2;
		}
	}
	php->a[php->size++] = x;//插入数据
	adjustup(php->a,php->size-1);  //向上调整

}
void adjustdown(HPDatatype* a, int parent,int size)//向下调整算法
{
	int child = parent * 2 + 1;//假设为左孩子
	while (child<size)
	{
		if (child+1<size&&a[child] < a[child + 1])//如果假设不成立,就为右孩子
		{
			child++;
		}
		if (a[parent] < a[child])//孩子大于父亲
		{
			swap(&a[parent], &a[child]);
			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,0,php->size);//向下调整算法
}
HPDatatype heaptop(HP* php)//取堆顶元素
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

void print(HP* php)
{
	int i = 0;
	for (i = 0; i < php->size; i++)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}
bool heapempty(HP* php)//判断是否为空
{
	assert(php);
	assert(php->size > 0);
	return php->size == 0;
}

int heapsize(HP* php)//数据个数
{
	assert(php);
	assert(php->size > 0);
	return php->size;
}
#include "heap.h"
int main()
{
	HP php;
	heapinit(&php);
	int arr[] = {27,15,19,18,28,34,65,49,25,37};
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		heappush(&php, arr[i]);
	}
	print(&php);
	int k = 5;
	while (k--)
	{
		printf("%d ", heaptop(&php));
		heappop(&php);
	}
	heapdestroy(&php);
	return 0;
}

3.test.c

#include "heap.h"
int main()
{
	HP php;
	heapinit(&php);
	int arr[] = {27,15,19,18,28,34,65,49,25,37};
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		heappush(&php, arr[i]);
	}
	print(&php);
	int k = 5;
	while (k--)
	{
		printf("%d ", heaptop(&php));
		heappop(&php);
	}
	heapdestroy(&php);
	return 0;
}

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

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

相关文章

[附源码]计算机毕业设计JAVA火车票订票管理系统

[附源码]计算机毕业设计JAVA火车票订票管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM myba…

【HDU No. 4006】 第k 大的数 The kth great number

【HDU No. 4006】 第k 大的数 The kth great number 杭电OJ 题目地址 【题意】 小明和小宝正在玩数字游戏。游戏有n轮&#xff0c;小明在每轮中都可以写一个数&#xff0c;或者问小宝第k 大的数是什么&#xff08;第k 大的数指有k -1个数比它大&#xff09;。 游戏格式为&am…

运维开发实践 - Docker - 容器实现原理

1.Docker容器是什么 按照Docker官网&#xff0c;容器是运行在宿主机上的一个进程&#xff0c;但与宿主机上的其他进程相隔离&#xff1b; 2.容器实现原理 这种隔离机制使用了内核中的namespace和cgroups功能&#xff1b; 2.1.Linux namespace Linux通过将系统的资源放置在…

Redis数据类型之string

文章目录stringⅠ. 增删查改Ⅱ. 加减操作Ⅲ. 数据生命周期Ⅳ. 业务场景 - 高频数据Ⅴ. 注意事项提示&#xff1a;以下是本篇文章正文内容&#xff0c;Redis系列学习将会持续更新 string ● 存储的数据&#xff1a;单个数据&#xff0c;最简单的数据存储类型&#xff0c;也是最常…

Paket在Linux下使用问题

1. 系统软件版本 Linux系统&#xff1a;Debian 10&#xff0c;需安装mono-devel Paket版本&#xff1a;7.1.5 关于Paket的介绍&#xff1a;https://github.com/fsprojects/Paket Paket的发布版本&#xff1a;https://github.com/fsprojects/Paket/releases 2. 使用方法 将…

原来背后都是商业利益,看到网易和暴雪的解约之后,原来是要定以后的KPI,坐地起价,但是一个时代已经结束了,都留在了记忆之中

1&#xff0c;大瓜新闻&#xff0c;2023年1月暴雪游戏中国将不会续约&#xff1f;&#xff1f; 2&#xff0c;原因是主要坐地起价&#xff0c;提高分成设置KPI 还好网易有自研游戏&#xff0c;估计早知道会有现在这样的情况。 提前做好了准备。还记得有个公司叫 九城吗&#x…

Linux操作系统~带你理解文件系统与软硬链接

目录 1.C语言中的FILE和文件描述符对应的file 2.Linux的EXT系列的文件系统 &#xff08;1&#xff09;.block group中六个部分的内容 inode索引结点相关 Q&#xff1a;这两个inode有什么不同&#xff1f; &#xff08;2&#xff09;.一个文件的inode和对应的block如何关联…

off-by-one (b00ks)

前言 个人简略记录&#xff0c;过程不详细 gdb开始调试 vmmap查看程序基地址为 0x555555400000 继续运行&#xff0c;输入auth search hollk关键字&#xff0c;auth存放的地址可以找出为&#xff1a;0x555555602040 圈起来的是溢出的 \x00 创建两个books 因为图书的结构体指…

数据库以及数据库常用概念、ER模型相关概念

目录 1.我们为什么要学习数据库&#xff1f; 2.数据库的相关概念&#xff1a; 2.关系型数据库和非关系型数据库的区别 A.关系型数据库&#xff1a; B.非关系型数据库&#xff1a; C.非关系型数据库的相关类别有以下几种&#xff1a; 3.关系型数据库设计规则 4.表、记录、…

华为数据中心VS技术理论讲解

目录 VS之间的关系 VS的配置管理权限 VS之间、VS与外部通信 VS资源分配 VS与其它技术的联合部署 VS的创建和删除 VS&#xff08;Virtual System&#xff09;虚拟系统是指将一台物理设备虚拟成多个相互隔离的逻辑设备 逻辑设备之间软硬件隔离&#xff0c;互不影响&#xff…

立体式校验保护,让你的系统避免 90% 以上的 bug

1. 概览 在实际开发过程中&#xff0c;数据校验是最为重要的一环&#xff0c;问题数据一旦进入系统&#xff0c;将对系统造成不可估量的损失。轻者&#xff0c;查询时触发空指针异常&#xff0c;导致整个页面不可用&#xff1b;重者&#xff0c;业务逻辑错误&#xff0c;造成流…

为了买个硬盘,我专门写了篇笔记

文章目录SSD是固态硬盘的一些参数插槽接口总线类型传输协议插槽接口/总线/协议关联参考连接HDD是机械硬盘&#xff1b;SSD是固态硬盘。 SSD是固态硬盘的一些参数 插槽接口 外观不同而已&#xff1b;插槽接口不影响传输速度&#xff0c;但是插槽接口不适配的话是肯定插不到电脑…

力扣(LeetCode)12. 整数转罗马数字(C++)

模拟 罗马数字和掰手指数数的区别在于&#xff0c;IV/IXIV/IXIV/IX 这类倒着数数的&#xff0c;和阿拉伯数字最大的区别在于 555 的 10k10^k10k 倍 k∈Nk\isin Nk∈N &#xff0c;需要被表示出来。所以除了记录 I/X/C/MI/X/C/MI/X/C/M ——1/10/100/10001/10/100/10001/10/100…

activiti-image-generator

activiti-image-generator目录概述需求&#xff1a;设计思路实现思路分析1.ActivitiImageException2.ProcessDiagramGenerator3.ProcessDiagramSVGGraphics2D4.ProcessDiagramDOMGroupManager5.DefaultProcessDiagramGenerator参考资料和推荐阅读Survive by day and develop by…

Smart point智能指针(part.1)

1&#xff1a;为什么出现智能指针   为了避免多个指针指向一个对象的时候 销毁其中一个point 其他的point就会变成空point 或者多次删除被指向对象而发生报错   或者单纯删除指针 不删除其指向的对象 当最后一个指向对象被删除的时候 对象依然存在 造成资源泄露  智能指针…

MATLAB continue语句

详细例子&#xff1a; 在MATLAB中建立一个脚本文件&#xff0c;并输入下述代码&#xff1a; a 10; %while loop execution while a < 20if a 15% skip the iteration a a 1;continue;endfprintf(value of a: %d , a);a a 1; end 运行该文件&#xff0c;显示下…

unet医学肺部ct图分割简单记录

UNet医学图像分割 说明&#xff1a; 本项目采用pytorch——gpu——cuda11.6本项目用的UNet网络架构一、硬件&#xff1a; Windows GPU 二、软件环境安装&#xff1a; pytorchNibabel 三、用法&#xff1a; 医学数据采集为kaggle的官网新冠肺炎ct图数据预处理&#xff1a;1…

青少年python系列 45.文件操作1

青少年python系列目录_老程序员115的博客-CSDN博客 青少年python教学视频ppt源码 在计算机信息时代&#xff0c;我们知道文本文件可存储的数据量多得难以置信&#xff0c;例如气象站的天气数据、交管部门的交通数据、金融商业街的社会经济数据、电子图书馆或博物馆的文学作品等…

做斗音都要经历的几个时期,你目前处于哪个阶段呢?

大家好&#xff0c;我是我赢助手&#xff0c;专注于自媒体短视频去水印、去重和文案提取运营。 今天给大家分享下做斗音的几个时期 1、兴奋期 听别人说斗音遍地是黄金开始到处学艺买资料准备大干一场。 这时是最关键的&#xff0c;能学到可以实操的项目才是最重要的&#x…

施耐德PLC TM218如何实现远程上传下载程序?

施耐德TM218支持IEC61131-3标准的六种编程语言&#xff0c;具备模块化、结构紧凑、功能全面等特点&#xff0c;在工业控制领域应用广泛&#xff0c;是市场上常见的产品之一&#xff0c;性价比较高。 因此&#xff0c;对于采购施耐德PLC的企业来说&#xff0c;通过PLC程序的上下…