【数据结构】模拟实现堆

1.思路

建堆

首先要写一个向下调整算法,运用向下调整算法的条件是,左右子树是对应的堆,我们以小根堆为例子。
写出向下调整算法之后,我们从叶子结点的双亲结点开始遍历
i结点的双亲结点为 (i-1)/2
i结点的左孩子为2i+1,右孩子为2i+2。(结点大小必须小于结点个数N)
在这里插入图片描述

堆插入

插入的时候首先要判断容量,不够要扩容,插入要插入到最后,然后比较插入的数据和双亲结点的大小,调用向上调整算法,使得新的结构为堆。
在这里插入图片描述

堆删除

删除我们是从堆顶开始删除,但是我们要保证删除之后,还是堆,也就是说,原来双亲和孩子是对应的,所以我们将顶的数据和最后一个数据位置调换,然后将数据个数减1,然后再用一次向下调整算法,使得删除后的调整为堆。
在这里插入图片描述

堆排序

堆排序的时候,先建堆,降序建小堆,然后将堆顶的数据和堆底的数据交换,然后数据向前移动一位,然后再用向下调整算法,直到移动到第一个数据。时间复杂度为Nlog

TOPK问题

先建一个大小为K的小堆,用随机数算法产生N个数据,储存在一个数组里,然后依次比较堆顶的数据和数组里(K–N)个数据的大小,比堆顶小,就将堆顶的删除,然后将这个数插入进去。最后得出的K个数就是最小的前K个。
2.代码展示

Heap.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<assert.h>
#include<string.h>
#include<time.h>
#define N  100000 
#define K   10


typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int  HeapEmpty(Heap* hp);
// 对数组进行堆排序
void HeapSort(int* a, int n);

Heap.c

#include"heap.h"

//交换函数
void Swap(int* p1,int* p2)	
{
	int tmp = *p2;
	*p2 = *p1;
	*p1 = tmp;
}

//向下调整
void AdjustDown(HPDataType* a, int n, int root)		
{
	assert(a);
	int parent = root;
	int child = 2 * parent + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] > a[child + 1])
		{
			++child;
		}

		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
		{
			break;
		}
	}
}

//建堆
void HeapCreate(Heap* hp, HPDataType* a, int n)  
{
	assert(hp && a);
	hp->_a = (HPDataType *)malloc(sizeof(HPDataType)*n);
	memcpy(hp->_a, a, sizeof(HPDataType)*n);
	hp->_capacity = n;
	hp->_size = n;
	
	for (int i = (hp->_size-2)/2; i >= 0; --i)  //从最后一个子节点找它的双亲节点。
	{
		AdjustDown(hp->_a,hp->_size	,i);
	}
}

//向上调整
void AdjustUp(HPDataType* a, int n)
{
	int child = n-1;
	int parent = (child - 1) / 2;
	while (child)
	{

		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}

	}
}

//增容
void Checkcapacity(Heap* hp)
{
	assert(hp);
	if (hp->_size == hp->_capacity)
	{
		
		HPDataType newcapacity = hp->_capacity == 0 ? 2 : 2 * hp->_capacity;
		hp->_a = (HPDataType*)realloc(hp->_a, sizeof(HPDataType)*newcapacity);//要用realloc追加
		hp->_capacity = newcapacity;
	}
}

//堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	Checkcapacity(hp);
	hp->_a[hp->_size] = x;
	++hp->_size;
	AdjustUp(hp->_a, hp->_size);
	
}

//堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);
	Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
	hp->_size--;
	for (int i = (hp->_size - 1) / 2; i >= 0; --i)
	{
		AdjustDown(hp->_a, hp->_size, i);
	}
}

//打印
void HeapPrint(Heap* hp)	//打印
{
	for (int i = 0; i < hp->_size; i++)
	{
		printf("%d ", hp->_a[i]);
	}
	printf("\n");
}

//取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	return hp->_a[0];
}

//堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->_size;
}

//堆的判空
int   HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->_size == 0 ? 0 : 1;

}

//堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	free(hp->_a);
	hp->_a = NULL;
	hp->_capacity = 0;
	hp->_size = 0;
}

//堆排序

void HeapSort(int* a, int n) //堆排序	降序用小堆
{
	assert(a);
	for (int i = (n - 2) / 2; i >= 0; --i)		//先建堆
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}

}

// TopK问题:找出N个数里面最大/最小的前K个问题。
// 比如:未央区排名前10的泡馍,西安交通大学王者荣耀排名前10的韩信,全国排名前10的李白。等等问题都是Topk问题,
// 需要注意:
// 找最大的前K个,建立K个数的小堆
// 找最小的前K个,建立K个数的大堆


void TestTopk()
{
	Heap hp;
	HPDataType*a = (HPDataType*)malloc(sizeof(HPDataType)*N); 
	srand((unsigned int)time(0));
	for (int i = 0; i < N; i++)
	{
		a[i] = rand() % 10;
	}
	HeapCreate(&hp, a, K); 
	for (int i = K; i < N; i++)
	{
		if ((HPDataType)HeapTop(&hp) < a[i])
		{
			HeapPop(&hp);
			HeapPush(&hp, a[i]);
		}
		
	}
	HeapPrint(&hp);
	
}

TestHeap()
{
	Heap hp;
	int a[] = { 27,15,19,18,28,34,65,49,25,37 };
	int n = sizeof(a) / sizeof(a[0]);
	HeapCreate(&hp,a,n);
	HeapPrint(&hp);
	HeapPush(&hp, 4);
	HeapPrint(&hp);
	HeapPop(&hp);
	HeapPrint(&hp);
	HeapPop(&hp);
	HeapPrint(&hp);
	HeapPop(&hp);
	HeapPrint(&hp);
	HeapDestory(&hp);
	HeapPrint(&hp);
	HeapEmpty(&hp);
	HeapSize(&hp);
	HeapSort(a, n);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
	
	
}

Heap.c

#include"heap.h"
int main()
{
	TestHeap();
	TestTopk();
	system("pause");
	return 0;
}

3.心得体会
最后堆销毁的时候,一直有问题,找了半天,最后才发现扩容的时候有问题。TOPK是建立大小为K的小堆,然后比较剩下的数和堆顶的大小,将顶删除,然后比堆顶大的数据插进去,堆的实现还是 比较简单的。

发布了79 篇原创文章 · 获赞 6 · 访问量 3802

猜你喜欢

转载自blog.csdn.net/qq_41152046/article/details/104620963