堆《数据结构》

1. 堆排序

1.1 建堆

建大堆
建小堆

向上调整建堆

AdjustUp建堆

在这里插入图片描述

void AdjustUp(HPDataType* a, int child)
{
	// 初始条件
	// 中间过程
	// 结束条件
	int parent = (child - 1) / 2;
	//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 headsport(int* a, int n)//建堆
{
	for (int i = 0;i < n;i++)
	{
		AdjustUp(a, i);//建堆(降序建大堆)向上调整建堆
	}
}
void test1()
{
	int arr[] = {1,5,3,2,7,9,4,6,8};
	headsport(arr, sizeof(arr) / sizeof(arr[0]));
	for (int i = 0;i < sizeof(arr) / sizeof(arr[0]);i++)
	{
			printf("%d", arr[i]);
	}

}

1:我们通过AdjustUp函数来建堆
2:在控制AdjustUp函数中a[child] < a[parent]的大小比较关系,这是控制建小堆,还是建大堆

如:a[child] < a[parent] 建小堆
在这里插入图片描述
a[child] > a[parent]建 大堆
在这里插入图片描述

向下调整建堆

AdjustDown

在这里插入图片描述
在这里插入图片描述

void AdjustDown(HPDataType* a, int n, int parent)
{
	// 先假设左孩子小
	int child = parent * 2 + 1;

	while (child < n)  // child >= n说明孩子不存在,调整到叶子了
	{
		// 找出小的那个孩子
		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;
		}
	}
}
void headsport(int* arr, int n)
{
	//时间复杂度O(N)
	for (int i = (n-1-1)/2;i >=0;i--)
	{
		AdjustDown(arr,n,i);//向下调整建堆
	}


}
void test1()
{
	int arr[] = {1,5,3,2,7,9,4,6,8};
	headsport(arr, sizeof(arr) / sizeof(arr[0]));
	for (int i = 0;i < sizeof(arr) / sizeof(arr[0]);i++)
	{
			printf("%d", arr[i]);
	}

}

与AdjustUp同理:
arr[child]>arr[parent]->大堆
在这里插入图片描述
arr[child]<arr[parent]->小堆
在这里插入图片描述

1.2 利用堆删除思想来进行排序

1:建堆
升序:建大堆
降序:建小堆
2:建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

实现图:
在这里插入图片描述

void headsport(int* arr, int n)
{
	for (int i = (n-1-1)/2;i >=0;i--)
	{
		AdjustDown(arr,n,i);//向下调整建堆
	}
	int end = n - 1;//取最后一位数据
    while (end > 0)
{
	Swap(&arr[0], &arr[end]);//交换
	AdjustDown(arr, end, 0);//在向下建堆
	end--;
}
}
void test1()
{
	int arr[] = {1,5,3,2,7,9,4,6,8};
	headsport(arr, sizeof(arr) / sizeof(arr[0]));
	for (int i = 0;i < sizeof(arr) / sizeof(arr[0]);i++)
	{
			printf("%d", arr[i]);
	}

}
int main()
{
	test1();
	//CreateNdDate();
	/*TestHeap();*/
	/*TestHeap3();*/
	return 0;
}

1.3Top-k问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据
量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

1.用数据集合中前K个元素来建堆

·前k个最的元素,则建小堆
·前k个最的元素,则建大堆

2.用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素

将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素

#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
void CreateNdDate()
{
	//造数据
	int n = 100000;
	srand(time(0));//随机种子
	const char* file = "daf.txt";
	FILE* fin = fopen(file, "w");//打开文件
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}
	for (int i = 0;i < n;i++)
	{
		int x = (rand() + i) % 1000000;//随机值
		fprintf(fin, "%d\n", x);//写入数据
	}
	fclose(fin);
}

void TestHeap()
{
	int k;//获取前k个数
	printf("请输入k>");
	scanf("%d", &k);
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("mallco fail");
		return;
	}
	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error ");
		return;
	}
	//读取文件前k个数
	for (int i = 0;i < k;i++)
	{
		fscanf(fout,"%d", &kminheap[i]);
	}
	for (int i = (k - 1 - 1) / 2;i >= 0;i--)//建小堆
	{
     AdjustDown(kminheap, k, i);
	}
	//读取剩下n-k个数
	int x = 0;
	while (fscanf(fout,"%d", &x)>0);
	{
		if (x > kminheap[0])
		{
			kminheap[0] = x;
			AdjustDown(kminheap, k, 0);
		}
	}
	printf("最大前k个\n");
	for (int i = 0;i < k;i++)
	{
		printf("%d\n", kminheap[i]);
	}

}
int main()
{
	
	CreateNdDate();
	TestHeap();
	
	return 0;
}

在这里插入图片描述

2.堆的时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个结点不影响最终结果):

向下调堆:从最后一个根节点开始调整
在这里插入图片描述
在这里插入图片描述

向上调堆:

调整次数:
第二层:向上调整 1次
第三层: 向上调整2次


第n层:向上调整n次

节点个数
第一层:2^0个
第二层:2^1个


第n层:2^n-1个

在这里插入图片描述

感谢大家观看!!!

评论 114
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值