当前位置: 首页 > article >正文

数据结构与算法:堆排序

堆排序的基本思想

堆排序的基本思想是将待排序的序列构造成一个大堆或者小堆,此时,整个序列的最大值(最小值)就是堆顶的根节点。将它移走(其实就是将其与数组的末尾元素交换,此时末尾元素就是最大最或最小值),然后将剩余的堆重新构造成一个堆,这样就会得到新的最大值(最小值)。如此反复执行,使能得到一个有序序列了。

具体实现时,首先需要根据给定的待排序数组构建一个初始堆。构建堆的过程通常是从最后一个非叶子节点开始,向上遍历每个节点,对每个节点进行下沉操作,以确保每个节点都满足堆的性质。(向下调整算法)  下沉操作是堆排序中的关键步骤,它通过比较节点与其子节点的值,确保父节点的值大于(对于大堆)或小于(对于小堆)其子节点的值。

在堆构建完成后,堆的根节点就是序列的最大(或最小)元素。将其与堆数组的最后一个元素交换,然后移除最后一个元素(或将其视为已排序部分),此时堆的大小减一。接着,对剩余部分重新进行下沉操作,以恢复堆的性质。这个过程重复进行,知道堆的大小减至1,此时序列已经完全排序。

堆排序的特性总结

  1. 堆排序使用堆来选数,效率高了很多
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(1)
  4. 稳定性:不稳定
  • 空间效率:堆排序是一种原地排序算法,这意味着它不需要额外的存储空间来辅助排序过程,除了原数组本身。这使得堆排序在处理大数据集时,相较于其他需要额外空间的排序算法,具有更高的空间效率。
  • 时间效率:堆排序的时间复杂度在最坏情况下为O(nlogn),其中n是待排序元素的数量。这意味着无论输入数据的初始状态如何,堆排序都能保持相对稳定的性能。这一点在处理大型数据集时尤为重要,因为某些排序算法(如快速排序)在特定输入情况下可能会退化为O(n²)的时间复杂度。
  • 不稳定性:堆排序是一种不稳定的排序算法。这意味着如果两个元素具有相同的值,它们在排序后的相对位置可能会改变。这在某些应用中可能是一个缺点,但在其他不需要保持元素相对位置不变的场景中则不是问题。

代码

#define _CRT_SECURE_NO_WARNINGS

#include "HeapSort.h"

void HeapSort(int* a, int n)
{
	//降序,建小堆
	//升序,建大堆
	
	向上调整  从数组下标为1的开始调
	//for (int i = 1; i < n; i++)
	//	AdjustUp(a, i);

	//向下调整 从第一个非叶子节点开始调
	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--;
	}
}

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustUp(int* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (a[child + 1] < a[child] && child + 1 < n)
			child++;
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}

测试

#define _CRT_SECURE_NO_WARNINGS

#include "HeapSort.h"

int main()
{
	int arr[] = { 3,1,9,5,7,2,6,4,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	HeapSort(arr, sz);
	return 0;
}

TopK问题

TopK算法是一种用于找出数据集中前K个最大或最小的元素的算法。‌它特别适用于处理大量数据,尤其是在内存或处理时间有限的情况下。TopK算法的核心思想是通过维护一个大小为K的堆来高效地找到前K个最大或最小的元素。‌

堆排序方法‌:这是实现TopK算法的一种常见方法。首先,使用数据集中的前K个元素建立一个堆(最小堆或最大堆,取决于需要找的是最大还是最小的K个元素)。然后,将剩余的元素逐一与堆顶元素进行比较,如果比堆顶元素大(或小),则替换并调整堆。最终,堆中的元素即为所求的前K个最大(或最小)元素。

//文件中找TOPk
//造数据
void CreateData()
{
	int n = 100000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen fail");
		return;
	}
	for (int i = 0; i < n; ++i)
	{
		int x = (rand() + i) % 10000000; //+i 减少重复
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}


void test1()
{
	//CreateData();

	//Top k
	int k;
	scanf("%d", &k);
	int* topk = (int*)malloc(sizeof(int) * k);
	if (topk == NULL)
	{
		perror("malloc fail");
		return;
	}

	const char* file = "data.txt";
	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	//读取文件中的前k个保存到数组中
	for (int i = 0; i < k; i++)
		fscanf(fout, "%d", &topk[i]);

	//建k个数的小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
		AdjustDown(topk, k, i);

	//读取剩下的 n - k 个数
	int x = 0;
	while (fscanf(fout, "%d", &x) > 0)
	{
		if (x > topk[0])
		{
			topk[0] = x;
			AdjustDown(topk, k, 0);
		}
	}
	
	//排序  向下调整
	int end = k - 1;
	while (end > 0)
	{
		Swap(&topk[0], &topk[end]);
		AdjustDown(topk, end, 0);
		end--;
	}
	//打印前k个
	printf("最大的前%d个:", k);
	for (int i = 0; i < k; i++)
	{
		printf("%d  ", topk[i]);
	}
}


void test2()
{
	int k;
	scanf("%d", &k);
	int arr[] = { 3,1,9,5,7,2,6,4,8,26538,6707,9105,30541,29850,31600,29234};
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	for(int i = (sz - 1 - 1) / 2; i >= 0; i--)
		AdjustDown(arr, sz, i);
	
	int end = sz - 1;
	while (end > 0)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, end, 0);
		end--;
	}


	//打印前k个
	printf("最大的前%d个:", k);
	for (int i = 0; i < k; i++)
	{
		printf("%d  ", arr[i]);
	}
}


http://www.kler.cn/a/573022.html

相关文章:

  • Android 14 - HDMI_CEC架构分析
  • 本地部署类似 ChatGPT 的大模型:基于 Ollama + Open-WebUI
  • XTDrone+Mavros+Gazebo仿真——配置与控制不同的无人机
  • html中几个符号的转义和还原
  • LeetCode 79: 单词搜索 (Word Search)
  • C++11中atomic
  • 【SpringBoot】一文讲懂什么是scanBasePackages
  • [MySQL初阶]MySQL(3)表的约束
  • 华为最新OD机试真题-计算堆栈中的剩余数字-Python-OD统一考试(E卷)
  • C语言学习笔记-进阶(1)深入理解指针3
  • Ollama+AnythingLLM安装
  • 期权有哪些用处?期权和期货比优势在哪?
  • CentOS 7 安装 Redis6.2.6
  • R语言绘图:韦恩图
  • 06. View工作原理
  • 《HarmonyOS赋能的智能影像诊断系统安全架构与临床实践》
  • 杨辉三角解法
  • kotlin的val声明的变量是常量吗
  • vscode 都有哪些大模型编程插件
  • Raven: 2靶场渗透测试