Heap堆

Heap堆排序

向上调整

当我们在一个堆的末尾插入一个数据后,需要对堆进行调整,使其仍然是一个堆,这时需要用到堆的向上调整算法。

向上调整算法的基本思想(以建小堆为例):

1.将目标结点与其父结点比较。

2.若目标结点的值比其父节点的值小,则交换目标节点与其父节点的位置,并将原目标节点的父节点当作新的目标节点继续进行向上调整。若目标节点的值比其父节点的值大,则停止向上调整,此时该树已经是小堆了。

向下调整

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根结点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

int array[] = {27,15,19,18,28,34,65,49,25,37};

堆的创建

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根结点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子结点的子树开始调整,一直调整到根结点的树,就可以调整成堆。

int a[] = {1,5,3,8,7,6}; 

上图,从节点3开始调,将36看作一个树来调整,然后再调节点5,当根节点的两个儿子节点都满足堆的性质,就可以对根节点进行向上/下调整了。

堆排序

建堆

利用堆删除的思想来进行排序

将首尾进行交换,然后向下调整(不考虑交换到后面的元素,将剩余的元素当作堆来调整)

Heap.h文件

#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 HeapPush(HP* php, HPDataType val);
void HeapPop(HP* php);
void AdjustUp(HPDataType* a,int child);
void AdjustDown(HPDataType* a,int sz ,int parent);
void Swap(HPDataType* p1, HPDataType* p2);
void SetHeap(HPDataType* a,int sz);
bool HeapEmpty(HP* php);
HPDataType HeapTop(HP* php);
int HeapSize(HP* php);

Heap.c文件

#include"Heap.h"

void HeapInit(HP* php)
{
	assert(php); // 判断指针是否为空

	php->a = (HPDataType*)malloc(sizeof(HPDataType) * 4); // 分配堆数组的内存空间
	if (php->a == NULL)
	{
		perror("malloc fail:"); // 输出错误信息
		exit(-1); // 退出程序
	}
	php->size = 0; // 初始化堆的大小为0
	php->capacity = 4; // 初始化堆的容量为4
}

void HeapPush(HP* php, HPDataType val)
{
	assert(php); // 判断指针是否为空

	if (php->size == php->capacity) // 如果堆已经满了,需要扩容
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * php->capacity * 2); // 重新分配内存空间
		if (tmp == NULL)
		{
			perror("realloc fail:"); // 输出错误信息
			exit(-1); // 退出程序
		}
		php->a = tmp; // 将指针指向新的内存空间
		php->capacity *= 2; // 更新堆的容量
	}
	php->a[php->size++] = val; // 将新元素插入到堆的最后面
	AdjustUp(php->a, php->size - 1); // 对插入的元素进行向上调整,保持堆的性质
}

void Swap(HPDataType* p1, HPDataType* p2)
{
	HPDataType tmp = *p1; // 交换两个元素的值
	*p1 = *p2;
	*p2 = tmp;
}

void HeapPop(HP* php)
{
	assert(php); // 判断指针是否为空
	assert(!HeapEmpty(php)); // 判断堆是否为空
	
	Swap(&php->a[0], &php->a[php->size - 1]); // 将第一个元素和最后一个元素交换位置
	php->size--; // 将堆的大小减1
	AdjustDown(php->a, php->size, 0); // 对堆进行向下调整,保持堆的性质
}

void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2; // 获取当前节点的父节点的下标
	while (a[parent] < a[child]) // 如果当前节点比父节点大,就需要进行交换
	{
		Swap(&a[child], &a[parent]); // 交换当前节点和父节点的值
		child = parent; // 更新当前节点的下标
		parent = (child - 1) / 2; // 更新父节点的下标
	}
}

void AdjustDown(HPDataType* a, int sz, int parent)
{
	assert(a); // 判断指针是否为空

	int child = parent * 2 + 1; // 获取当前节点的左儿子的下标
	while (child < sz) // 如果当前节点有左儿子,就需要进行调整
	{
		if (child + 1 < sz && a[child] < a[child + 1]) // 如果当前节点有右儿子,并且右儿子比左儿子大,就需要选取右儿子进行交换
		{
			child++;
		}

		if (a[child] > a[parent]) // 如果当前节点比父节点大,就需要进行交换
		{
			Swap(&a[child], &a[parent]); // 交换当前节点和父节点的值
			parent = child; // 更新父节点的下标
			child = parent * 2 + 1; // 更新当前节点的下标
		}
		else
		{
			break; // 如果当前节点比父节点小,就不需要进行调整了,直接退出循环
		}
	}
}

void SetHeap(HPDataType* a, int sz)
{
	assert(a); // 判断指针是否为空

	for (int i = 0; i < sz; i++)
	{
		AdjustUp(a, i); // 对堆进行向上调整,保持堆的性质
	}
}

bool HeapEmpty(HP* php){
	assert(php); // 判断指针是否为空

	return php->size == 0; // 如果堆的大小为0,就说明堆为空
	}

	HPDataType HeapTop(HP* php)
	{
		assert(php); // 判断指针是否为空

		return php->a[0]; // 返回堆的第一个元素,也就是最大值
	}

	int HeapSize(HP* php)
	{
		assert(php); // 判断指针是否为空

		return php->size; // 返回堆的大小
	}

test.c文件

#include"Heap.h"

void heap_sort(HPDataType* arr,int sz)
{
	int num = sz;
	SetHeap(arr, sz);
	while (sz)
	{
		Swap(&arr[0], &arr[sz-1]);
		sz--;
		AdjustDown(arr, sz-1, 0);
	}
	for (int i = 0; i < num; i++)
	{
		printf("%d ", arr[i]);
	}

}

int main(void)
{
	/*
	HP hp;
	HeapInit(&hp);
	HeapPush(&hp, 4);
	HeapPush(&hp, 18);
	HeapPush(&hp, 42);
	HeapPush(&hp, 12);
	HeapPush(&hp, 2);
	HeapPush(&hp, 3);
	HeapPush(&hp, 100);
	HeapPush(&hp, 200);
	HeapPush(&hp, 300);
	HeapPush(&hp, 87);
	HeapPush(&hp, 68);
	HeapPush(&hp, 33);
	HeapPop(&hp);
	while (!HeapEmpty(&hp))
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
	*/
	int arr[] = { 1,2,5,3,4,7,8,3,2,8,100,23,52,123,666 };
	int sz= sizeof(arr) / sizeof(arr[0]);
	//printf("%d ", sz);
	heap_sort(arr,sz);
	return 0;
}

posted @ 2023-08-24 22:28  Hayaizo  阅读(2)  评论(0编辑  收藏  举报