【数据结构】二叉树--堆排序

目录

[一 降序(建小堆)](#一 降序(建小堆))

[二 升序 (建大堆)](#二 升序 (建大堆))

[三 优化(以升序为例)](#三 优化(以升序为例))

[四 TOP-K问题](#四 TOP-K问题)


一 降序(建小堆)

cpp 复制代码
void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = 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 (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 HeapSort(int* a, int n)
{
	int end = n - 1;
	int i = 0;
	//建堆
	for (i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}

	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

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

二 升序 (建大堆)

cpp 复制代码
//升序 建大堆
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 (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 HeapSort(int* a, int n)
{
	int end = n - 1;
	int i = 0;
	//建堆
	for (i = 1; i < n; i++)
	{
		AdjustUp(a, i);
	}

	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}

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

三 优化(以升序为例)

可以用向下建堆的方法

cpp 复制代码
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[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}

		else
		{
			break;
		}
	}
}

//void HeapSort(int* a, int n)
//{
//	int end = n - 1;
//	int i = 0;
//	//建堆
//	for (i = 0; i < n; i++)
//	{
//		AdjustUp(a, i);
//	}
//
//	while (end > 0)
//	{
//		Swap(&a[0], &a[end]);
//		AdjustDown(a, end, 0);
//		end--;
//	}
//}


void HeapSort(int* a, int n)
{
	int end = n - 1;
	int i = 0;
	//建大堆
	for (i = (n-1-1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		end--;
	}
}
int main()
{
	int a[] = { 2, 3, 5, 7, 4, 6, 8, 65, 100, 70, 32, 50, 60};
	HeapSort(a, sizeof(a) / sizeof(int));
	return 0;
}

这样建堆的方式对时间复杂度有什么优化吗?

四 TOP-K问题

TOP - K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。

比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。

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

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

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

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

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

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

typedef int HPDataType;

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


void AdjustDown(HPDataType* 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[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}

		else
		{
			break;
		}
	}
}

void PrintTopK(const char* filename, int k)
{
	//1 建堆--用a中前K个元素建堆(小堆)
	FILE* fout = fopen(filename, "r");
	if (fout == NULL)
	{
		perror("fopen fail");
		return;
	}

	int* minheap = (int*)malloc(sizeof(int) * k);
	if (minheap == NULL)
	{
		perror("malloc fail");
		return;
	}

	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minheap[i]);
	}

	//前K个建小堆
	for (int i = (k-2) / 2; i >= 0; i--)
	{
		AdjustDown(minheap, k, i);
	}

	//2 将剩余n-k元素与堆顶元素比较, 满足就交换
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
		if (x > minheap[0])
		{
			//替换进堆
			minheap[0] = x;
			AdjustDown(minheap, k, 0);
		}
	}

	for (int i = 0; i < k; i++)
	{
		printf("%d ", minheap[i]);
	}
	printf("\n");
	fclose(fout);

}

void CreateDate()
{
	//造数据
	int n = 100000;
	srand(time(0));
	const char* file = "data.txt";
	FILE* fin = fopen(file, "w");
	if (fin == NULL)
	{
		perror("fopen error");
		return;
	}

	for (int i = 0; i < n; i++)
	{
		int x = (rand() + i) % n;
		fprintf(fin, "%d\n", x);
	}

	fclose(fin);
}

int main()
{
	CreateDate();
	PrintTopK("data.txt", 5);
	return 0;
}

本节对前面的二叉树基础很高, 没有理解的, 可以翻看我之前对二叉树顺序结构及其实现的章节.

继续加油!

相关推荐
麻瓜老宋8 小时前
AI开发C语言应用按步走,表达式计算器calc的第二十二步,分号赋值链式修复、TOKEN_ASSIGN
c语言·开发语言·atomcode
小poop10 小时前
轮转数组:从暴力到最优,一题掌握算法复杂度分析
数据结构·算法·leetcode
SilentSlot12 小时前
【C/C++】手写 DPDK 协议栈(十一):基于 PPS、SYN 比例和源 IP 熵的 DDoS 检测
c语言·c++·tcp/ip
wabs66613 小时前
关于图论【卡码网104.建造最大岛屿的思考】
数据结构·算法·图论
冻柠檬飞冰走茶14 小时前
PTA基础编程题目集 7-15 计算圆周率(C语言实现)
c语言·开发语言·数据结构·算法
Rabitebla16 小时前
C++ 内存管理全面复习:从内存分布到 operator new/delete
java·c语言·开发语言·c++·算法·leetcode
SilentSlot17 小时前
【C/C++】手写 DPDK 协议栈(八):用五元组 Hash 加速连接定位
c语言·c++·哈希算法
Tisfy17 小时前
LeetCode 3517.最小回文排列 I:排序(Python两行版) / 计数(O(n)时间+O(C)空间+字符串原地修改)
c语言·python·leetcode·字符串·排序·计数排序·回文
菜鸟的升级路18 小时前
C语言栈刷题:LeetCode 20 有效的括号完整解题笔记
c语言·笔记·leetcode
冻柠檬飞冰走茶18 小时前
PTA基础编程题目集 7-13 日K蜡烛图(C语言实现)
c语言·开发语言·数据结构·算法