数据结构之堆

文章详细介绍了堆的概念,包括大根堆和小根堆的定义,以及如何使用C语言实现堆的初始化、插入、删除、判断堆是否为空、获取堆顶元素、获取堆的大小、打印堆数据以及销毁堆等操作。堆的插入和删除通过调整算法来维护堆的性质。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构之堆

一、堆的定义和结构
概念

当k1、k2、k3……kn的数据按照完全二叉树的方式存放在数组中,且这个完全二叉树满足某个节点的值总是不大于(或不小于)其父节点的值时,称该完全二叉树为堆。

  • 根节点最大的叫大根堆
  • 根节点最小的叫小根堆
  • 堆的某个节点总是不大于或者不小于其父节点的值
  • 堆是一个完全二叉树

在这里插入图片描述

二、堆的实现
1.结构定义

我们通过实际存储结构可以看出,堆的存储和顺序表都是存在数组中的,所以他们的结构十分相似。

  • 一个动态开辟的数组
  • 一个存储空间的大小
  • 一个实际存储空间的大小
typedef struct Heap
{
	int* a;
	int size;
	int capacity;
}HP;
2.堆的初始化
  • 和顺序表一样
void HeapInit(HP* hp)
{
	assert(hp);
	hp->capacity = hp->size = 0;
	hp->a = NULL;
}
3.堆的插入
  • 堆的插入永远是从尾部插入
  • 插入后数据后必须调整数据顺序,保持大堆或者小堆,即进行向上调整(也就是建堆)。
void Heap_pushBack(HP* hp, int x)
{
	assert(hp);
	if (hp->capacity == hp->size) {
		int NewCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		int* tmp = (int*)realloc(hp->a, sizeof(int) * NewCapacity);
		if (tmp == NULL) {
			perror("relloc fail");
			exit(-1);
		}
		hp->a = tmp;
		hp->capacity = NewCapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;

	AdjustUp(hp, hp->size-1);
}
  • 先把数据插入堆的末尾(此时是小堆)
  • 向上插入: 要是子节点小于父节点则进行交换,直到该节点到一个父节点比他小的位置。

在这里插入图片描述

void AdjustUp(HP* hp, int child)
{
	assert(hp);
	int parent = (child - 1) / 2;
	while (child > 0) {
		if (hp->a[child] < hp->a[parent]) {
			Swap(&hp->a[child], &hp->a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else {
			break;
		}
	}
}

要是要建大堆,修改交换条件即可。

  • 父节点必须大于子节点
if (hp->a[child] > hp->a[parent]) {
			Swap(&hp->a[child], &hp->a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
4.判断堆是否为空
bool HeapEmpty(HP* hp)
{
	assert(hp);
	return hp->size == 0;
}
5.堆的删除
  • 堆的删除只能删除堆顶数据

但是删除堆顶数据后,我们又需要挪动数据,且其中堆的大小关系完全被破坏

  • 于是有了向下调整算法

1.交换堆顶和堆的最后一个数据,删除最后一个数据(也就是交换后的堆顶数据)。

2.再向下调整数据,恢复堆的顺序结构

void Heap_popBack(HP* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	Swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	AdjustDown(&hp->a, hp->size, 0);
}
void AdjustDown(HP* hp, int size, int parent)
{
	assert(hp);
	int minChild = parent * 2 + 1;
	while (minChild < size) {
		if(minChild + 1 < size && 
		hp->a[minChild] > hp->a[minChild + 1])
			minChild++;
		if (hp->a[minChild] < hp->a[parent]) {
			Swap(&hp->a[minChild], &hp->a[parent]);
			parent = minChild;
			minChild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

要是要建大堆,修改交换条件即可。

  • 父节点必须大于子节点
if(minChild + 1 < size && 
		hp->a[minChild] < hp->a[minChild + 1])
			minChild++;
		if (hp->a[minChild] > hp->a[parent]) {
			Swap(&hp->a[minChild], &hp->a[parent]);
			parent = minChild;
			minChild = parent * 2 + 1;
		}
6.topK数据
  • 判断堆是否为空
  • 栈顶实际就是第一个节点
int HeaptopK(HP* hp)
{
	assert(!HeapEmpty(hp));
	return hp->a[0];
}
7.堆的个数
  • 判空
  • 返回结构体中的size
int HeapSize(HP* hp)
{
	assert(!HeapEmpty(hp));
	return hp->size;
}
8.数据打印
  • 方便测试代码是否正确
  • 遍历打印即可
void HeapPrint(HP* hp)
{
	assert(hp);
	for (int i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->a[i]);
	}
	printf("\n");
}
9.堆的销毁
void HeapDestory(HP* hp)
{
	assert(hp);
	free(hp->a);
	hp->capacity = hp->size = 0;
}
三、完整代码
1.heap.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef struct Heap
{
	int* a;
	int size;
	int capacity;
}HP;

void HeapInit(HP* hp);
void Heap_pushBack(HP* hp, int x);
void Heap_popBack(HP* hp);
void Swap(int* a, int* b);
int HeaptopK(HP* hp);
2.heap.c
#include "Heap.h"

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

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

void AdjustUp(HP* hp, int child)
{
	assert(hp);
	int parent = (child - 1) / 2;
	while (child > 0) {
		if (hp->a[child] < hp->a[parent]) {
			Swap(&hp->a[child], &hp->a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else {
			break;
		}
	}
}

void Heap_pushBack(HP* hp, int x)
{
	assert(hp);
	if (hp->capacity == hp->size) {
		int NewCapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		int* tmp = (int*)realloc(hp->a, sizeof(int) * NewCapacity);
		if (tmp == NULL) {
			perror("relloc fail");
			exit(-1);
		}
		hp->a = tmp;
		hp->capacity = NewCapacity;
	}
	hp->a[hp->size] = x;
	hp->size++;

	AdjustUp(hp, hp->size-1);
}

void AdjustDown(HP* hp, int size, int parent)
{
	assert(hp);
	int minChild = parent * 2 + 1;
	while (minChild < size) {
		if(minChild + 1 < size && hp->a[minChild] < hp->a[minChild + 1])
			minChild++;
		if (hp->a[minChild] > hp->a[parent]) {
			Swap(&hp->a[minChild], &hp->a[parent]);
			parent = minChild;
			minChild = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

bool HeapEmpty(HP* hp)
{
	assert(hp);
	return hp->size == 0;
}

void Heap_popBack(HP* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	Swap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	AdjustDown(&hp->a, hp->size, 0);
}

void Swap(int* a, int* b)
{
	int* tmp = *a;
	*a = *b;
	*b = tmp;
}

int HeapSize(HP* hp)
{
	assert(!HeapEmpty(hp));
	return hp->size;
}

int HeaptopK(HP* hp)
{
	assert(!HeapEmpty(hp));
	return hp->a[0];
}

void HeapDestory(HP* hp)
{
	assert(hp);
	free(hp->a);
	hp->capacity = hp->size = 0;
}
3.test.c
#include "Heap.h"

int main()
{
	HP hp;
	HeapInit(&hp);
	Heap_pushBack(&hp, 7);
	Heap_pushBack(&hp, 6);
	Heap_pushBack(&hp, 20);
	Heap_pushBack(&hp, 2);
	Heap_pushBack(&hp, 5);
	Heap_pushBack(&hp, 15);
	Heap_pushBack(&hp, 13);
	Heap_pushBack(&hp, 12);
	Heap_pushBack(&hp, 0);
	Heap_pushBack(&hp, 1);

	int a = HeaptopK(&hp);
	HeapPrint(&hp);

	Heap_popBack(&hp);
	HeapPrint(&hp);

	int b = HeaptopK(&hp);

	printf("%d %d \n", a, b);
	HeapDestory(&hp);
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值