堆的实现---增,删,查,改,堆排序,TopK问题(自用)

Posted 你快看看我

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了堆的实现---增,删,查,改,堆排序,TopK问题(自用)相关的知识,希望对你有一定的参考价值。

堆排序

1.堆的概念及结构

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

2.堆的性质:

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

3.堆的实现

typedef int HPDataType;
typedef struct Heap

 HPDataType* a;
 int size;
 int capacity;
Heap;

(1)堆调整向下算法

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

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


交换函数:

void Swap(int* p1, int* p2)

	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;

void AdjustDown(int* a, int n, int parent)

	int child = parent * 2 + 1;
	while (child < n) 
	
		if (child + 1 < n && a[child + 1] > a[child]) 
		
			++child;
		

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

(*)向下调整算法时间复杂度

假设这个堆为满二叉树,堆高为h,假设每层高度为hi,假设每层结点数为ni,则建堆的时间复杂度为t(n)=Σni*hi,即:

(2)堆调整向上算法

void AdjustUp(int* a, int n, int child)

	int parent = (child-1)/2;
	while (child > 0)
	
		if (a[child] > a[parent])
		
			Swap(&a[parent], &a[child]);
			child=parent;
			parent = (child - 1) / 2;
		
		else
		
			break;
		
	

(3)堆的创建

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

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

void HeapCreate(Heap* hp, HPDataType* a, int n)

	assert(hp);
	hp->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (hp->a == NULL)
	
		printf("malloc fail\\n");
		exit(-1);
	

	memcpy(hp->a, a, sizeof(HPDataType) * n);
	hp->size = n;
	hp->capacity = n;

	for (int i = (hp->size - 1 - 1) / 2; i >= 0; --i)
	
		AdjustDown(hp->a, hp->size, i);
	

(4)堆的销毁

void HeapDestory(Heap* hp)

	assert(hp);
	free(hp->a);
	hp->size = hp->capacity = 0;

(5)堆的插入

先插入一个80到数组的尾上,再进行向上调整算法,直到满足堆。

void HeapPush(Heap* hp, HPDataType x)

	assert(hp);
	if (hp->size == hp->capacity)
	
		HPDataType* tmp = (HPDataType*)realloc(hp->a, hp->size * 2 * sizeof(HPDataType));
		if (tmp == NULL)
		
			printf("malloc fail\\n");
			exit(-1);
		
		hp->a = tmp;
		hp->capacity *= 2;
	

	hp->a[hp->size - 1] = x;
	hp->size++;
	AdjustUp(hp->a, hp->size, hp->size-1);

(6)堆的删除

删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

void HeapPop(Heap* hp)

	assert(hp);
	assert(hp->size>0);
	Swap(&hp->a[0], &hp->a[hp->size-1]);
	hp->size--;
	AdjustDown(hp->a, hp->size, 0);

(6)取堆顶的数据

HPDataType HeapTop(Heap* hp)

	assert(hp);
	assert(hp->size > 0);
	return hp->a[0];

(7)堆的数据个数

int HeapSize(Heap* hp)

	assert(hp);
	return hp->size;

(8)堆的判空

bool HeapEmpty(Heap* hp)

	assert(hp);
	return hp->size==0;

(9)堆的打印

int HeapPrint(Heap* hp)//打印堆

	for (int i = 0; i < hp->size; i++)
	
		printf("%d ", hp->a[i]);
	
	printf("/n");

	int num = 0;
	int leveSize = 1;
	for (int i = 0; i < hp->size; i++)
	
		printf("%d ", hp->a[i]);
			num++;
		if (num == leveSize)
		
			printf("\\n");
			leveSize *= 2;
			num = 0;
		
	

(10)对数组进行堆排序

void HeapSort(int* a, int n)

	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	
		AdjustDown(a, n, i);
	

	int end = n - 1;
	while (end > 0)
	
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	

4.堆的应用

(1)TopK问题

题目描述:

输入整数数组 arr ,找出其中最小的 k 个数

示例:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

实现思路:

第一步:先取出数组中的前K个数据,建大堆
第二步:依次剩下N-K个数,和堆顶的数据比较。如果比堆顶的数据小,则替换堆顶的数据,然后调整堆(向下调整法)
第三步:最后堆里的就是最小的K个数
时间复杂度:O(N*logK)
空间复杂度:O(K)

实现代码:

void Swap(int* p1, int* p2)

	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;



void AdjustDown(int* a, int n, int parent)

	int child = parent * 2 + 1;
	while (child < n) 
    
		if (child + 1 < n && a[child + 1] > a[child]) 
		
			++child;
		

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



int* getLeastNumbers(int* arr, int arrSize, int k, int* returnSize)

    if(k==0)
    
        *returnSize=0;
        return NULL;
    

    int* arrRet=(int*)malloc(sizeof(int)*k);
    for(int i=0;i<k;i++)
    
        arrRet[i]=arr[i];
    

    for(int j=(k-1-1)/2;j>=0;j--)
    
        AdjustDown(arrRet,k,j);
    

    for(int i=k;i<arrSize;i++)
    
        if(arr[i]<arrRet[0])
        
            arrRet[0]=arr[i];
            AdjustDown(arrRet,k,0);
        
    
    *returnSize=k;
    return arrRet;

以上是关于堆的实现---增,删,查,改,堆排序,TopK问题(自用)的主要内容,如果未能解决你的问题,请参考以下文章

排序4-堆排序与海量TopK问题

数据结构之堆以及topk问题

图文最详细的堆解析:从二叉树到堆到解析大根堆小根堆,分析堆排序,最后实现topK经典面试问题

TopK问题与堆排序

TopK问题与堆排序

TopK问题与堆排序