堆的概念与实现

目录

一、堆的介绍

1.堆的概念

2.堆的性质:

3.堆的结构

二、堆的实现

1.堆的定义

2.接口函数

三、堆的实现

1.堆的初始化

2.堆的销毁

3.获取堆顶数据

4.判断堆是否为空

[5. 堆的插入](#5. 堆的插入)

向上调整算法(重点)

向下调整算法(重点)

6.删除堆顶元素

7.堆的大小

四、完整代码

heap.h

heap.c


一、堆的介绍

1.堆的概念

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

2.堆的性质:

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

 堆总是一棵完全二叉树。

3.堆的结构

二、堆的实现

1.堆的定义

复制代码
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

注:这里实现的是数组堆

2.接口函数

复制代码
void HeapInit(HP* php);
void HeapDestroy(HP* php);
//插入x继续保持堆形态
void HeapPush(HP* php, HPDataType x);
//删除堆顶元素
void HeapPop(HP* php);
HeapTop(HP* php);
//返回堆顶元素
HPDataType HeapTop(HP* php);
//判空
bool HeapEmpty(HP* php);
//堆的大小
int HeapSize(HP* php);
//向下调整
void AdjustDown(HPDataType* a, int n, int parent);
//向上调整
void AdjustUp(HPDataType* a, int child);
void Swap(HPDataType* p1, HPDataType* p2);

三、堆的实现

1.堆的初始化

复制代码
void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->size = php->capacity = 0;
}

2.堆的销毁

复制代码
void HeapDestroy(HP* php)
{
	free(php->a);
	free(php);
	php = NULL;
}

3.获取堆顶数据

复制代码
//返回堆顶元素
HPDataType HeapTop(HP* php)
{
	
	assert(php);
	return php->a[0];
}

4.判断堆是否为空

复制代码
bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

5. 堆的插入

插入的核心思路

① 先将元素插入到堆的末尾,即最后一个孩子之后。

② 插入之后如果堆的性质遭到破坏,就将新插入的节点顺着其的父亲往上调整到合适位置。直到调到符合堆的性质为止。

根据堆的性质,如果不满足大堆和小堆,出现子大于父或父大于子的情况,为了保证插入之后堆还是堆,我们就需要进行自下往上的调整。堆插入数据对其他节点没有影响,只是可能会影响从它到根节点路径上节点的关系。

向上调整算法(重点)

复制代码
void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType* tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}

		else
		{
			break;
		}
	}
}

父亲的下标=(孩子的下标-1)/2,知道了这个,就很好操作了,最坏的情况就是要调到根的位置,即parent=child,这里之所以是chile>0,而不是parent<0,是因为当child=0时,parent依然等于0,不可能小于0的。

向下调整算法(重点)

现在我们给出一个数组,逻辑上看作一棵完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆

向下调整算法的基本思想(小堆):

 1.从根结点处开始,选出左右孩子中值较小的孩子。

 2.让小的孩子与其父亲进行比较。

 若小的孩子比父亲还小,则该孩子与其父亲的位置进行交换。并将原来小的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。

 若小的孩子比父亲大,则不需处理了,调整完成,整个树已经是小堆了。

我们首先定义一个minChild,这个是左孩子和右孩子中较小的孩子,parent和它进行交换。

这里调整中有两个结束条件,第一个是父亲小于孩子就停止,第二个是调整到叶子节点了,也就是minChild>n就调整完毕。

复制代码
void AdjustDown(HPDataType* a, int n, int parent)
{
	int minChild = parent * 2 + 1;
	while (minChild < n)
	{
		if ((minChild+1)<n&&a[minChild] > a[minChild + 1])
		{
			minChild++;
		}
		if (a[parent] > a[minChild])
		{
			Swap(&a[parent], &a[minChild]);
			parent = minChild;
			minChild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

}

使用堆的向下调整算法,最坏的情况下(即一直需要交换结点),需要循环的次数为:h - 1次(h为树的高度)。而h = (N为树的总结点数)。所以堆的向下调整算法的时间复杂度为:O(logN) 。

6.删除堆顶元素

复制代码
//删除堆顶元素 -- 找次打或者次小
void HeapPop(HP* php)
{ 
	assert(php);
	assert(!HeapEmpty(php));
	Swap(&(php->a[0]),&(php->a[php->size-1]) );
	php->size--;

	AdjustDown(php->a, php->size, 0);
}

直接删除堆顶元素,会破坏堆的结构,所以这里我们让堆顶元素和最后一个元素进行交换,然后size--,重新向下调整,构建堆。

7.堆的大小

复制代码
int HeapSize(HP* php)
{
	return php->size;
}

四、完整代码

heap.h

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

void HeapInit(HP* php);
void HeapDestroy(HP* php);
//插入x继续保持堆形态
void HeapPush(HP* php, HPDataType x);
//删除堆顶元素
void HeapPop(HP* php);
HeapTop(HP* php);
//返回堆顶元素
HPDataType HeapTop(HP* php);
//判空
bool HeapEmpty(HP* php);
//堆的大小
int HeapSize(HP* php);
//向下调整
void AdjustDown(HPDataType* a, int n, int parent);
//向上调整
void AdjustUp(HPDataType* a, int child);
void Swap(HPDataType* p1, HPDataType* p2);

heap.c

复制代码
#define  _CRT_SECURE_NO_WARNINGS
#define  _CRT_SECURE_NO_WARNINGS
#include "heap.h"

void HeapPrint(HP* php)
{
	for (int i = 0; i < php->size; ++i)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

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

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType* tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}

		else
		{
			break;
		}
	}
}

void AdjustDown(HPDataType* a, int n, int parent)
{
	int minChild = parent * 2 + 1;
	while (minChild < n)
	{
		if ((minChild+1)<n&&a[minChild] > a[minChild + 1])
		{
			minChild++;
		}
		if (a[parent] > a[minChild])
		{
			Swap(&a[parent], &a[minChild]);
			parent = minChild;
			minChild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}

}




//插入x继续保持堆形态
void HeapPush(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");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size] = x;
	php->size++;

	//向上调整
	AdjustUp(php->a, php->size - 1);

}
//删除堆顶元素 -- 找次打或者次小
void HeapPop(HP* php)
{ 
	assert(php);
	assert(!HeapEmpty(php));
	Swap(&(php->a[0]),&(php->a[php->size-1]) );
	php->size--;

	AdjustDown(php->a, php->size, 0);
}
//返回堆顶元素
HPDataType HeapTop(HP* php)
{
	
	assert(php);
	return php->a[0];
}
bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}
int HeapSize(HP* php)
{
	return php->size;
}
相关推荐
dtq04247 小时前
数据结构 - 堆
数据结构
nhdh13 小时前
B+树揭秘:MySQL索引核心原理全解析
数据结构·b树·mysql
海上小飞龙14 小时前
【KMP算法-下篇】同一道题:Java 库函数 2600 微秒,手写 KMP 16 微秒
java·开发语言·算法
青少儿编程课堂14 小时前
后缀自动机解析:本质不同子串与最长重复片段统计
c++·python·算法·bfs·信息学竞赛
朝朝辞暮i14 小时前
C++ 第 12 课:局部变量、作用域、变量生命周期
开发语言·c++·算法
乐迪信息15 小时前
港口船舶逆行怎么管?AI防爆摄像机搭载智能检测算法
大数据·人工智能·深度学习·算法·计算机视觉
Sirens.16 小时前
Java并发锁详解:六类锁策略与 synchronized 底层原理
java·前端·算法
和裕17 小时前
全纸结构重型纸箱能否满足 1 吨以上设备出口熏蒸豁免要求?通关合规性全解析
大数据·运维·网络·人工智能·算法
tangguofeng17 小时前
不带头结点的链队操作集(不含InitFlag)(C语言版)
算法
苏打豆18 小时前
CRISP源码阅读——基于ROS2力矩反馈控制器与速度加速度零空间
c++·线性代数·算法·矩阵·机器人