堆的概念与实现

目录

一、堆的介绍

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;
}
相关推荐
chenziang126 分钟前
leetcode hot100 环形链表2
算法·leetcode·链表
Captain823Jack2 小时前
nlp新词发现——浅析 TF·IDF
人工智能·python·深度学习·神经网络·算法·自然语言处理
Captain823Jack2 小时前
w04_nlp大模型训练·中文分词
人工智能·python·深度学习·神经网络·算法·自然语言处理·中文分词
Aileen_0v03 小时前
【AI驱动的数据结构:包装类的艺术与科学】
linux·数据结构·人工智能·笔记·网络协议·tcp/ip·whisper
是小胡嘛3 小时前
数据结构之旅:红黑树如何驱动 Set 和 Map
数据结构·算法
m0_748255023 小时前
前端常用算法集合
前端·算法
呆呆的猫3 小时前
【LeetCode】227、基本计算器 II
算法·leetcode·职场和发展
Tisfy4 小时前
LeetCode 1705.吃苹果的最大数目:贪心(优先队列) - 清晰题解
算法·leetcode·优先队列·贪心·
余额不足121384 小时前
C语言基础十六:枚举、c语言中文件的读写操作
linux·c语言·算法
yuanManGan5 小时前
数据结构漫游记:静态链表的实现(CPP)
数据结构·链表