堆的实现(C语言)

什么是堆?

可以简单理解为:1.堆中某个节点的值总是不大于或不小于其父节点的值2.堆总是一棵完全二叉树3.将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。 理解了堆的概念后那么下面就来创建一个堆。

假设有这样一个数组:int array[]={8,2,3,4,5,6},存储的是完全二叉树,那么:

 从上图中可以发现,除了根节点外,其余部分满足小堆的基本性质。因为:2<4,    2<5;    3<6。那么如何将上面的完全二叉树调整为一个小堆呢?

首先,用根节点的值和它的两个孩子节点值进行比较,如果根节点的值比孩子节点的值大,那么就和两个孩子节点中值较小的进行交换,一直重复上述过程,就将其变成了堆(小堆)。图解如下:

下面来写代码:

void Swap(int* a, int* b){//交换两个数的值
	*a ^= *b;
	*b ^= *a;
	*a ^= *b;
}
void BuildHeap(int *array, int parent, int size){//向下调整
	int child = parent * 2 + 1;//找到左孩子
	while (child<size){
		if (child + 1 < size&&array[child]>array[child+1]){//右孩子更小,就更换
			child += 1;
		}
		if (array[parent]>array[child]){
			Swap(&array[parent], &array[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else{
			return;
		}
	}
}

 在堆的这种结构中,在这里给定第一个元素的下标是0。任意一个节点的下标为i,那么它的左孩子节点的下标就是2*i+1。

程序中要注意:1.孩子节点的下标不超过数组长度。2.每次进行交换时是交换根节点和孩子节点中值较小的,交换完毕后更新child和parent的值。

上述代码中对于堆的构建是建立在除了根节点外其余节点都满足堆的性质的条件下,那么如果给定一组乱序的值,该如何来构建堆(小堆)?

根据上述代码,可以发现只要根节点下面的元素满足堆的性质,那么就可以用上述代码来构建堆。所以当给定乱序的值后,首先找到倒数第一个非叶子节点,调整其与它的孩子节点构成堆,然后依次找到之前的非叶子节点,在调整为堆,直到找到最后一个非叶子节点(根节点),进行最后一次调整。简单图示如下:

那么代码很简单了,写一个循环,然后在调用上述代码就可以了:

for (int i = (size - 2) / 2; i>=0; i--){//size是数组长度
		BuildHeap(hp->array,i, size);
	}

 那么堆的构建就基本完成了,接下来就是对堆的 插入、删除等一系列操作了。

1.插入:将元素插在堆尾,然后在进行向上调整,且向上调整时只需要比较当前位置的值与双亲节点的值就可以了,要注意,此时这个堆是大堆还是小堆。代码如下:

void Heapjust(int *array, int size){//向上调整
	int child = size - 1;
	int parent = (child-1) / 2;
	while (child>0){//child等于零说明到了堆顶,不用调整了
		if (array[parent]>array[child){
			Swap(&array[child], &array[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else{
			return;
		}
	}
}

这里注意,child变成0的时候就找到了数组的第一个元素,此时向上调整已经完成。

2.删除:删除时要求要删除的是堆顶元素,但是不能直接删除,因为这样就直接破坏了堆的结构。那么删除时应该首先把堆顶元素和堆尾元素进行交换,然后忽略堆尾元素,在对其余元素进行一次堆的构建就可以了。

这些操作完成后,其余对堆的操作就很简单,这里就不赘述了。下面来看完整代码,利用顺序表进行存储。且在堆的构建中函数中加入了函数指针,进行回调。回调函数由我们自己提供,用以来决定创建大堆还是小堆。代码如下:

1.Heap.h

#pragma once 
#include <stdio.h>
#include <windows.h>
#include <string.h>
#include <assert.h>
typedef int DataType;
typedef int(*Callback)(int, int);//这里Callback是函数指针类型
typedef struct Heap{
	DataType * array;
	int size;
	int capacity;
	Callback Call;
}Heap;
extern void HeapCreat(Heap *hp, DataType *a, int n, Callback Call);
extern void HeapPop(Heap *hp);
extern int HeapEmpty(Heap *hp);
extern void HeapPush(Heap *hp, DataType x);
extern DataType HeapTop(Heap *hp);
extern void HeapDestory(Heap *hp);
extern int Heapsize(Heap *hp);

2.Heap.c

#include "Heap.h"
void Swap(DataType* a, DataType* b){//交换两个数的值
	*a ^= *b;
	*b ^= *a;
	*a ^= *b;
}
void BuildHeap(DataType *array, int parent, int size, Callback Call){//向下调整
	DataType child = parent * 2 + 1;//找到左孩子
	while (child<size){
		if (child + 1 < size&&Call(array[child],array[child+1])){//右孩子更小,就更换
			child += 1;
		}
		if (Call(array[parent], array[child])){
			Swap(&array[parent], &array[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else{
			return;
		}
	}
}
void HeapCreat(Heap *hp, DataType *a, int n, Callback Call){//创建堆
	assert(a);
	assert(hp);
	hp->array = (DataType *)malloc(sizeof(DataType)*n);
	if (hp == NULL){
		perror("malloc");
		exit(0);
	}
	memcpy(hp->array, a, sizeof(DataType)*n);
	hp->size = n;
	hp->capacity = n;
	hp->Call = Call;
	for (int i = (hp->size - 2) / 2; i>=0; i--){
		BuildHeap(hp->array,i, hp->size,hp->Call);
	}
}
void Expansion(Heap *hp){//扩容空间
	hp->array = (DataType *)realloc(hp->array, sizeof(DataType)*(hp->capacity + 5));
	if (hp->array == NULL){
		perror("malloc");
		return;
	}
	hp->capacity += 5;
}
void Heapjust(DataType *array, int size, Callback Call){//向上调整
	int child = size - 1;
	int parent = (child-1) / 2;
	while (child>0){//child等于零说明到了堆顶,不用调整了
		if (Call(array[parent], array[child])){
			Swap(&array[child], &array[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else{
			return;
		}
	}
}
void HeapPush(Heap *hp, DataType x){//插入元素
	assert(hp);
	if (hp->size == hp->capacity){
		Expansion(hp);
	}
	hp->array[hp->size] = x;
	hp->size++;
	Heapjust(hp->array, hp->size,hp->Call);//调用向上调整函数
}
void HeapPop(Heap *hp){//删除堆顶元素
	if (!HeapEmpty(hp)){
		Swap(&hp->array[0],&hp->array[hp->size-1]);
		hp->size--;
		BuildHeap(hp->array,0 , hp->size,hp->Call);
	}
}
DataType HeapTop(Heap *hp){//获取堆顶元素
	assert(hp);
	if (!HeapEmpty(hp)){
		return hp->array[0];
	}
}
int Heapsize(Heap *hp){//获取元素个数
	assert(hp);
	return hp->size;
}
int HeapEmpty(Heap *hp){//判空
	assert(hp);
	return hp->size == 0;
}
void HeapDestory(Heap *hp){//销毁堆
	assert(hp);
	free(hp->array);
	hp->array = NULL;
	hp->size = 0;
	hp->capacity = 0;
}

3.main.c

#include "Heap.h"
int ComInt(DataType x,DataType y){//回调函数
	if (x > y){
		return 1;
	}
	return 0;
}
int main(){
	Heap  s;
	Heap *hp = &s;
	DataType array[] = {45,21,36,98,74};
	int lengh = sizeof(array) / sizeof(array[0]);
	HeapCreat(hp, array, lengh, ComInt);
	HeapPush(hp, 35);
	HeapPush(hp, 26);
	HeapPush(hp, 18);
	HeapPush(hp, 64);
	HeapPop(hp);
	HeapDestory(hp);
	system("pause");
	return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值