堆的实现(C语言)

堆是一个完全二叉树;

它的底层就是一个一维数组;

它的形式还是一个二叉树;

堆的特点的总结:

大堆和小堆的特点就是 我们的父亲和孩子的大小的关系;

大堆: 就是我们的父亲是不小于孩子;

小堆:就是我们的父亲是不大于孩子;

下标的关系:

左孩子的下标child = parent * 2 + 1

右孩子的下标就是左孩子加1;

结构体的成员:

我们知道底层是一个数组,那我们自然会想到顺序表的成员;

没错我们堆的成员和顺序表的成员是完全一样的;

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

功能代码的实现:

插入元素:

我们是按照顺序表的结构来作为底层的;所以我们直接尾插;因为尾插效率比头插效率高多了;

但是我们要保证堆结构的特点,所以我们必须调整我们尾部插入的元素;所以我们要实现一个向上调整的函数

// 堆的插入
void HeapPush(Heap* php, HPDataType x) {
	assert(php);
	if (php->capacity == php->size) {
		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;
	AdjustUp(php->a, php->size);
	php->size++;
}
向上调整:

我们先建立一个大堆;

我们堆的插入都是先尾插;因为我们底层是数组,所以我们在根据堆的特点来进行调整;

那我们先实现大堆的向上调整;就是当我们父亲小于孩子的时候我们进行调整;

我们知道要处理的元素的位置,在我们的最后;那我们怎么找到他的父亲呢?

,这个公式不用记忆,直接推导;因为我的整数会趋零截断;所以我们的左孩子和右孩子带人公式会指向同一个位置;

那我们怎么找到我们结束的条件呢?

(1)我们可以在我们的要调整的元素小于父亲的时候,就结束;

这个好实现直接遇见了就跳出循环;

(2)我们调整到了堆顶就结束;

我们怎么知道到堆顶了? 我们的底层是一个数组呀,所以我们堆顶就是我们的数组的下标为0的位置;

这样我们就可以写代码了;

//向上调整
void AdjustUp(HPDataType* a, int child) {
	assert(a);
	while (child > 0) {
		int parent = (child - 1) / 2;
		if (a[parent] < a[child]) {
			swap(&a[parent], &a[child]);
			child = parent;
		}
		else {
			break;
		}
	}
}
堆的删除:

我们是按照顺序表的结构来作为底层的;所以我们直接尾删;因为尾删效率比头插效率高多了;

我们就直接交换头尾的元素,然后将size减1,就是顺序表的删除了;

但是我们要保证堆结构的特点,所以我们必须调整我们头部的元素;所以我们要实现一个向下调整的函数


// 堆的删除
void HeapPop(Heap* php) {
	assert(php && php->size);
	swap(&(php->a[0]), &(php->a[php->size - 1]));
	php->size--;
	if(php->size>0)  AdjustDown(php->a, php->size, 0);
}
向下调整:

我们是要实现一个大堆,所以我们要比较头部节点和孩子中最小的的大小;如果我们比孩子中最小的还小;我们将进行调整;但是我们知道堆是一个完全二叉树,所以我们肯定是先有左孩子,右孩子是否存在不一定;怎么快速确认呢?很简单,利用他是完全二叉树的特点,只要他的右孩子后面还有节点,那他的右孩子一定存在;最简单的判断方法就是看他的右孩子的位置是否在数组大小里面;

那我们的循环结束条件呢?

(1)要调整的节点到了最底层,我们判断的方法就是他没有孩子,根据底层来说,就是他孩子的下标超过了数组的大小;

这个就是parent*2+1<size就是循环条件;

(2)要调整的节点并不小于最小的孩子了;

这个就是MIN>=parent

//向下调整
void AdjustDown(HPDataType* a, int size, int parent) {
	while (parent *2+1 < size) {
		int child = parent * 2 + 1;
		int rightChild = child + 1;
		int minIndex = child;
		if (rightChild < size && a[rightChild] < a[child]) {
			minIndex = rightChild;
		}
		if (a[parent] > a[minIndex]) {
			swap(&a[parent], &a[minIndex]);
			parent = minIndex;
		}
		else {
			break;
		}

	}
}

总的大堆的代码:

#pragma once
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>

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

//按照数组进行打印
void Print(Heap* php);
//交换函数
void swap(HPDataType* a, HPDataType* b);
//堆的初始化
void HeapInit(Heap* php);
// 堆的销毁
void HeapDestory(Heap* php);
//向上调整
void AdjustUp(HPDataType* a, int child);
// 堆的插入
void HeapPush(Heap* php, HPDataType x);
//向下调整
void AdjustDown(HPDataType* a, int size, int parent);
// 堆的删除
void HeapPop(Heap* php);
// 取堆顶的数据
HPDataType HeapTop(Heap* php);
// 堆的数据个数
int HeapSize(Heap* php);
// 堆的判空
bool HeapEmpty(Heap* php);
#include"Heap.h"
//交换函数
void swap(HPDataType* a, HPDataType* b) {
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
//堆的初始化
void HeapInit(Heap* php) {
	assert(php);
	php->a = NULL;
	php->capacity = php->size = 0;
}
// 堆的销毁
void HeapDestory(Heap* php) {
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}
//向上调整
void AdjustUp(HPDataType* a, int child) {
	assert(a);
	while (child > 0) {
		int parent = (child - 1) / 2;
		if (a[parent] < a[child]) {
			swap(&a[parent], &a[child]);
			child = parent;
		}
		else {
			break;
		}
	}
}
// 堆的插入
void HeapPush(Heap* php, HPDataType x) {
	assert(php);
	if (php->capacity == php->size) {
		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;
	AdjustUp(php->a, php->size);
	php->size++;
}
//按照数组进行打印
void Print(Heap* php) {
	assert(php);
	int i = 0;
	while (i < php->size) {
		printf("%d", php->a[i++]);
	}
	printf("\n");
}
//向下调整
void AdjustDown(HPDataType* a, int size, int parent) {
	while (parent *2+1 < size) {
		int child = parent * 2 + 1;
		int rightChild = child + 1;
		int minIndex = child;
		if (rightChild < size && a[rightChild] < a[child]) {
			minIndex = rightChild;
		}
		if (a[parent] > a[minIndex]) {
			swap(&a[parent], &a[minIndex]);
			parent = minIndex;
		}
		else {
			break;
		}

	}
}
// 堆的删除
void HeapPop(Heap* php) {
	assert(php && php->size);
	swap(&(php->a[0]), &(php->a[php->size - 1]));
	php->size--;
	if(php->size>0)  AdjustDown(php->a, php->size, 0);
}
// 取堆顶的数据
HPDataType HeapTop(Heap* php) {
	assert(php && php->size);
	return php->a[0];
}
// 堆的数据个数
int HeapSize(Heap* php) {
	assert(php);
	return php->size;
}
// 堆的判空
bool HeapEmpty(Heap* php) {
	assert(php);
	return php->size==0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值