数据结构之堆

数据结构之堆

文章目录

      • 数据结构之堆
        • 一、堆的定义和结构
          • 概念
        • 二、堆的实现
        • 1.结构定义
        • 2.堆的初始化
        • 3.堆的插入
        • 4.判断堆是否为空
        • 5.堆的删除
        • 6.topK数据
        • 7.堆的个数
        • 8.数据打印
        • 9.堆的销毁
        • 三、完整代码
          • 1.heap.h
        • 2.heap.c
        • 3.test.c

一、堆的定义和结构

概念

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

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

数据结构之堆_第1张图片

二、堆的实现

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

数据结构之堆_第2张图片

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 
#include 
#include 
#include 
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;
}

数据结构之堆_第3张图片

你可能感兴趣的:(初阶数据结构,数据结构,算法,java,c++)