1. 熟悉堆的概念以及特性

如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为 小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值; 堆总是一棵完全二叉树。


2. 总结数据结构中堆与内存堆区的区别

数据结构中的一般称“栈(stack)”,是一种后进先出的数据结构。它是一种概念,或者说是一种逻辑技术,与语言、平台无关。

内存管理中的“堆栈”其实是分为堆(heap)和栈(stack)的,以引用变量为例,引用变量本身存储在栈中,引用变量指向的值存储在堆中。

如int[] arr = {1, 2, 3};

变量arr(数组名)存储在栈中,变量arr的值(数组元素)存储在堆中(普通结构)。

内存管理中的栈采用的就是数据结构中的栈的思想,即遵循后进先出的管理方法。

好比数据结构中的栈是一项先进的技术,在内存管理中采用了该技术,在CPU的调度中可能也采用这种技术。

3. 实现堆的以下操作
// 要求:用户可以控制创建大堆还是小堆
typedef int DataType;

typedef struct Heap
{
DataType* _array;
int _capacity;
int _size;
}Heap;

// 创建堆
void CreatHeap(Heap* hp, int* array, int size);

//交换
void Swap(DataType *pLeft, DataType *pRight)
{
	int temp = *pLeft;
	*pLeft = *pRight;
	*pRight = temp;
}

//向下调整
void AdjustDown(int *array, int size, int parent)
{
	//默认parent的左孩子比较小
	int child = parent * 2 + 1;

	while (child < size)
	{
		//找左右孩子中较小的孩子
		if (((child + 1) < size) && (array[child + 1] < array[child]))
			child += 1;

		//检测parent是否满足堆的性质
		if (array[child] < array[parent])
		{
			Swap(&array[child], &array[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			return;
	}
}

// 创建堆 O(Nlog2(N))
void CreatHeap(Heap* hp, int* array, int size)
{
	//初始化Heap
	hp->_array = (DataType *)malloc(size * sizeof(DataType));
	if (hp->_array == NULL)
	{
		assert(0);
		return;
	}

	//O(N)
	hp->_capacity = size;

	for (int i = 0; i < size; ++i)
		hp->_array[i] = array[i];

	hp->_size = size;

	//找到倒数第一个非叶子节点
	int last = (size - 2) / 2;

	while (last >= 0)
	{
		AdjustDown(hp->_array, hp->_size, last);	//log2(N) 以2为底
		last--;
	}
}



// 向堆中插入值为data的元素
void InsertHeap(Heap* hp, DataType data);

void CheckCapacity(Heap* hp)
{
	assert(hp);
	if (hp->_capacity == hp->_size)
	{
		int newCapacity = 2 * hp->_capacity;
		//1.申请新空间
		DataType *pTemp = (DataType*)malloc(sizeof(DataType)*newCapacity);
		if (pTemp == NULL)
		{
			assert(pTemp);
			return;
		}
		//2.拷贝元素
		for (int i = 0; i < hp->_size; ++i)
			pTemp[i] = hp->_array[i];
		//3.释放旧空间
		free(hp->_array);
		hp->_array = pTemp;
		hp->_capacity = newCapacity;
	}
}

//向上调整
void AdjustUp(DataType* array, int size, int child)
{
	int parent = (child - 1) / 2;

	while (child != 0)
	{
		if (array[child] < array[parent])
		{
			Swap(&array[child], &array[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			return;
	}
}

// 向堆中插入值为data的元素 log2(N)
void InsertHeap(Heap* hp, DataType data)
{
	CheckCapacity(hp);
	hp->_array[hp->_size] = data;
	hp->_size++;
	AdjustUp(hp->_array, hp->_size, hp->_size - 1);
}


// 删除堆顶元素
void EraseHeap(Heap* hp);

// 删除堆顶元素 log2(N)
void EraseHeap(Heap* hp)
{
	if (EmptyHeap(hp))
		return;

	Swap(&hp->_array[0], &hp->_array[hp->_size - 1]);
	hp->_size--;
	AdjustDown(hp->_array, hp->_size, 0);
}


// 获取堆中有效元素个数
int SizeHeap(Heap* hp);

// 获取堆中有效元素个数 
int SizeHeap(Heap* hp)
{
	assert(hp);
	return hp->_size;
}


// 检测堆是否为空
int EmptyHeap(Heap* hp);

// 检测堆是否为空 
int EmptyHeap(Heap* hp)
{
	assert(hp);
	return hp->_size == 0;
}


// 获取堆顶元素
DataType TopHeap(Heap* hp);

// 获取堆顶元素 
DataType TopHeap(Heap* hp)
{
	assert(hp);
	return hp->_array[0];
}


// 销毁堆
void DestroyHeap(Heap* hp);

// 销毁堆 
void DestroyHeap(Heap* hp)
{
	assert(hp);
	free(hp->_array);
	hp->_capacity = 0;
	hp->_size = 0;
}

测试:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "Heap.h"

void Printf(Heap* hp)
{
	for (int i = 0; i < hp->_size; ++i)
	{
		printf("%d ", hp->_array[i]);
	}

	printf("\n");
}



int main()
{
	Heap hp;
	int array[] = { 27,15,34,25,28,19,65,49,18,37 };

	//创建堆
	CreatHeap(&hp, array, sizeof(array) / sizeof(array[0]));
	Printf(&hp);
	//15 18 19 25 28 34 65 49 27 37

	printf("%d\n", SizeHeap(&hp));	//10
	printf("%d\n", TopHeap(&hp));	//15
	//插入元素5
	InsertHeap(&hp, 5);
	Printf(&hp);
	//5 15 19 25 18 34 65 49 27 37 28
	printf("%d\n", SizeHeap(&hp));	//11
	printf("%d\n", TopHeap(&hp));	//5

	//删除堆顶元素
	EraseHeap(&hp);
	Printf(&hp);
	//15 18 19 25 28 34 65 49 27 37
	printf("%d\n", SizeHeap(&hp));	//10
	printf("%d\n", TopHeap(&hp));	//15
	
	//销毁
	DestroyHeap(&hp);
	return 0;
}


4. 用函数指针对堆进行扩展,使用户根据需要去创建大堆还是小堆

给堆结构中加入函数指针

typedef int DataType;

int Less(DataType left, DataType right);
int Greater(DataType left, DataType right);

//函数指针
typedef int(*PCMP)(DataType left, DataType right);

typedef struct Heap
{
	DataType* _array;
	int _capacity;
	int _size;
	PCMP _pPCMP;
}Heap;

// 创建堆 
void CreatHeap(Heap* hp, int* array, int size,PCMP pCMP);

需要修改的函数

我们需要通过函数指针去控制构建的是大堆还是小堆,在函数里面需要使用到比较大小的部分都使用

int Less(DataType left, DataType right)
{
	return left < right;
}

int Greater(DataType left, DataType right)
{
	return left > right;
}

//交换
void Swap(DataType *pLeft, DataType *pRight)
{
	int temp = *pLeft;
	*pLeft = *pRight;
	*pRight = temp;
}

//向下调整
void AdjustDown(int *array, int size, int parent, PCMP pCMP)
{
	//默认parent的左孩子比较小
	int child = parent * 2 + 1;

	while (child < size)
	{
		//找左右孩子中较小的孩子
		if (((child + 1) < size) && (pCMP(array[child + 1], array[child])))
			child += 1;
		//检测parent是否满足堆的性质
		if (pCMP(array[child], array[parent]))
		{
			Swap(&array[child], &array[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			return;
	}
}

// 创建堆 O(Nlog2(N))
void CreatHeap(Heap* hp, int* array, int size, PCMP pCMP)
{
	//初始化Heap
	hp->_array = (DataType *)malloc(size * sizeof(DataType));
	if (hp->_array == NULL)
	{
		assert(0);
		return;
	}

	//O(N)
	hp->_capacity = size;
	hp->_pPCMP = pCMP;

	for (int i = 0; i < size; ++i)
		hp->_array[i] = array[i];

	hp->_size = size;

	//找到倒数第一个非叶子节点
	int last = (size - 2) / 2;

	while (last >= 0)
	{
		AdjustDown(hp->_array, hp->_size, last, hp->_pPCMP);	//log2(N) 以2为底
		last--;
	}
}

void CheckCapacity(Heap* hp)
{
	assert(hp);
	if (hp->_capacity == hp->_size)
	{
		int newCapacity = 2 * hp->_capacity;
		//1.申请新空间
		DataType *pTemp = (DataType*)malloc(sizeof(DataType)*newCapacity);
		if (pTemp == NULL)
		{
			assert(pTemp);
			return;
		}
		//2.拷贝元素
		for (int i = 0; i < hp->_size; ++i)
			pTemp[i] = hp->_array[i];
		//3.释放旧空间
		free(hp->_array);
		hp->_array = pTemp;
		hp->_capacity = newCapacity;
	}
}

//向上调整
void AdjustUp(DataType* array, int size, int child, PCMP pCMP)
{
	int parent = (child - 1) / 2;

	while (child != 0)
	{
		if (pCMP(array[child], array[parent]))
		{
			Swap(&array[child], &array[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			return;
	}
}

// 向堆中插入值为data的元素 log2(N)
void InsertHeap(Heap* hp, DataType data)
{
	CheckCapacity(hp);
	hp->_array[hp->_size] = data;
	hp->_size++;
	AdjustUp(hp->_array, hp->_size, hp->_size - 1, hp->_pPCMP);
}

// 删除堆顶元素 log2(N)
void EraseHeap(Heap* hp)
{
	if (EmptyHeap(hp))
		return;

	Swap(&hp->_array[0], &hp->_array[hp->_size - 1]);
	hp->_size--;
	AdjustDown(hp->_array, hp->_size, 0, hp->_pPCMP);
}

运行

CreatHeap(&hp, array, sizeof(array) / sizeof(array[0]) Less);    //小堆

 

CreatHeap(&hp, array, sizeof(array) / sizeof(array[0]) Greater);    //大堆

 


5. 堆的应用:堆排序&TOPK问题等

堆排序

void HeapAdjust(int *array, int size, int parent)
{
	int child = parent * 2 + 1;

	while (child < size)
	{
		//在左右孩子中找较小的孩子
		if ((child + 1) < size && (array[child + 1] < array[child]))
			child += 1;

		if (array[child] < array[parent])
		{
			Swap(&array[child], &array[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			return;
	}
}

//时间复杂度:O(Nlog2(N))
//空间复杂度:O(1)
void HeapSort(int* array, int size)
{
	//1.建堆
	//从倒数第一个非叶子节点的位置开始,使用向下调整,一直到根节点
	int last = (size - 2) / 2;
	while (last >= 0)
	{
		HeapAdjust(array, size, last);		//向下调整
		last--;
	}

	//2.排序
	int end = size - 1;
	while (end != 0)
	{
		Swap(&array[0], &array[end]);
		HeapAdjust(array, end, 0);
		end--;
	}
}

TopK问题

需要一组数据前K个最大的元素,先用K个元素创建小堆,取元素与堆顶元素比较,大于则替换堆顶元素并重新调整堆,最后堆内的K个元素就是前K个最大的
需要一组数据前K个最小的元素,先用K个元素创建大堆,取元素与堆顶元素比较,小于则替换堆顶元素并重新调整堆,最后堆内的K个元素就是前K个最小的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值