数组实现堆(完全二叉树)的实现(向下调整算法以及TopK问题)

堆是用完全二叉树来实现,而它的节点是一层一层连在一起,所以我们可以用数组来实现他,将完全二叉树的节点按照堆的性质通过算法组合就实现了堆

堆的结构体以及函数声明

#pragma once

#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<assert.h>
//实现一个大堆
typedef int HpDataType;
typedef struct Heap
{
	HpDataType* _a;
	int  _size;
	int  _capacity;
}Heap;
//堆排序
void HeapSort(int *a, int n);
//向下调整算法
void AdjustDown(HpDataType* a, int n, int root);
//向上调整算法
void AdjustUp(HpDataType* a, int n, HpDataType x);
//有些需求,上来就需要把数组变成堆。
void HeapInit(Heap* php, HpDataType *a,int n);
//打印
void HeapPrint(Heap* hp);
//摧毁
void Destory(Heap* php);
//数组尾插入
void HeapPush(Heap* php, HpDataType x);
//删除堆顶数据,删数组尾没意义
void HeapPop(Heap* php);
//堆顶
HpDataType HeapTop(Heap* php);
// 堆的数据个数
int Heapsize(Heap* php);
// 堆的判空
int HeapEmpty(Heap* php);

//TopK问题
//最大的前K个,建立小堆
void PrintMaxTopK(HpDataType* a, int n, int k);


堆的初始化

在主函数里声明堆变量,初始化直接写一个函数,因为我们的堆肯定是要动态增长的,用栈里的数组空间不够,也不能动态增长

//堆的初始化
void HeapInit(Heap* php, HpDataType *a, int n)
{
	php->_a = (HpDataType*)malloc(sizeof(HpDataType)*n);
	memcpy(php->_a, a, sizeof(HpDataType)*n);
	php->_size = n;
	php->_capacity = n;
	//构建堆
	//但是直接调用AdjustDown函数能完成吗?不能。因为它传进来数组,并没能满足左右子树都是小堆
	//从最后一个节点的父亲开始调整,最后一个节点下标为n-1,父亲的坐标(n-1-1)/2
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(php->_a, php->_size, i);
	}

}

向下调整算法

小堆为例

void AdjustDown(HpDataType* a,int n,int root)
{
	int parent = root;
	int child = parent * 2 + 1;
	while (child < n)
	{
		//默认两个孩子中左孩子最小
		//如果有右孩子小于左孩子,child++,变成右孩子
		//因为后面要与两个孩子中最小的一个进行交换,比最小的小,也就比另一个孩子小

		//这里child+1小于n,是因为一种情况只有左孩子,而if语句里面+1数组越界。
		if ((child+1<n) &&(a[child + 1] <a[child]))
		{
			
			child++;
		}
		if (a[child] < a[parent])
		{
			Swap(&a[child] ,& a[parent]);
			//继续迭代
			//换下标,到下一颗子树
			parent = child;
			child = parent * 2 + 1;
		}
		//如果父亲节点比孩子小,而他本身结构是小堆
		else
		{
			break;
		}
	}	
}

向上调整算法

push插入数据时需要向上调整保证队的性质不会发生改变

void AdjustUp(HpDataType* a, int n, int child)
{
	int parent = (child - 1) / 2;
	//由于它是分数计算出来的所以永远不会小于0
	/*while (parent >= 0)*/
	while (child>0)
	{
		if (a[child]<a[parent])
		{
			Swap(&a[child], &a[parent]);
			//下标,孩子向上走
			child = parent;
			//你也继续上去,下次循环继续比较
			parent = (child - 1) / 2;
		}
		else
		{
			//说明不需要上去了
			break;
		}
	}
	

}

堆排序

void HeapSort(int *a, int n)
{
	//要进行堆排序,先把数组变成堆
	//从最后一个非叶子节点开始,叶子节点没必要向下调整
	//外面初始化已经建好堆了
	/*for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a,n,i);
	}
*/
	//这个代码虽然也可以,但是不好看,也不巧妙
	/*int count = n;
	for (int i = count; i >0; i--)
	{
		int end = n - 1;
		Swap(&a[0],&a[end]);
		n--;
		AdjustDown(a, n, 0);
	}*/
	int end = n - 1;
	//小于等于0结束,大于0继续
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		//堆的大小每次减一,每次从堆顶开始。(因为除了根节点都是小堆可以直接使用向下调整)
		AdjustDown(a, end, 0);
		end--;
	}
}

在这里插入图片描述

TopK问题

寻找最大或者最小的K个数。
这里以寻找最大的K个数为例:
建立小堆


//建立小堆,与堆顶的数据比较,比他大就把他换了
void PrintMaxTopK(HpDataType* a, int n, int k)
{
	Heap hp;
	//建立K个元素的小堆
	HeapInit(&hp, a, k);
	
	for (int i = k; i < n; i++)
	{
		
		if (a[i]>hp._a[0])
		{
			//最开始把HpDataType ret = HeapTop(&hp);放在了这里,找了半天,实际原因很简单,每次变的是ret,而不是实际堆顶的数据
			//放在这里每次都初始化一次,自然就出错了
			hp._a[0] = a[i];
			AdjustDown(hp._a, k, 0);
		}

	}
	
	for (int i = 0; i < k; i++)
		printf("%d ", hp._a[i]);
}

详细代码

堆的实现

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楠c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值