IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 堆的构建及应用~C语言简单实现 -> 正文阅读

[数据结构与算法]堆的构建及应用~C语言简单实现

目录

堆的实现

堆的创建

?建堆时间复杂度

堆的插入

堆的删除

堆的代码实现

堆的初始化

??堆的销毁

堆的插入

?堆的删除

获取堆顶的数据?

?堆的数据个数

堆的判空?

堆的打印?

向上调整

向下调整?

堆的应用

堆排序

TOP-K问题


堆(Heap)计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。

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

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

堆的实现

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

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

堆的创建

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

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


?

?建堆时间复杂度

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

?因此:建堆的时间复杂度为O(N)

堆的插入

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

堆的删除

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

?

堆的代码实现

先看看具体需要实现哪些功能

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;//大小
	int capacity;//最大容量
}Heap;
//堆的初始化
void HeapInit(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
//堆的打印
void HeapPrint(Heap*hp);
//向上调整(大堆/小堆)
void AdjustUp(HPDataType* a, int n, int child);
//向下调整(大堆/小堆)
void AdjustDown(HPDataType*a,int n,int root);
//交换
void swap(HPDataType*a, HPDataType*b);

下面来看看具体实现

堆的初始化

void HeapInit(Heap* hp, HPDataType* a, int n)
{
	assert(hp&&a);
	hp->a = (HPDataType*)malloc(sizeof(HPDataType)*n);
	hp->size = n;
	hp->capacity = n;
	for (int i = 0; i < n; i++)
	{
		hp->a[i] = a[i];
	}
	for (int i=(n-2)/2;i>=0;i--)
	{
		AdjustDown(hp->a,hp->size,i);
	}
}

??堆的销毁

void HeapDestory(Heap* hp)
{
	assert(hp);
	hp->size = 0;
	hp->capacity = 0;
	free(hp->a);
	hp->a = NULL;
}

堆的插入

void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	//扩容
	if (hp->size == hp->capacity)
	{
		hp->capacity *= 2;
		hp->a = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * hp->capacity);
	}
	hp->a[hp->size] = x;
	hp->size++;
	//向上调整
	AdjustUp(hp->a,hp->size,hp->size-1);
}

?堆的删除

// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);
	//先把第一个和最后一个交换
	swap(&hp->a[0], &hp->a[hp->size - 1]);
	//删掉最后一个
	hp->size--;
	//向下调整
	AdjustDown(hp->a,hp->size,0);
}

获取堆顶的数据?

// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	return hp->a[0];
}

?堆的数据个数

// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->size;
}

堆的判空?

// 堆的判空
int HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->size == 0 ;
}

堆的打印?

//堆的打印
void HeapPrint(Heap*hp)
{
	assert(hp);
	for (int i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->a[i]);
	}
	printf("\n");
}

?

向上调整

//向上调整(大堆)
void AdjustUp(HPDataType* a, int n, int child)
{
	assert(a);
	int parent = (child - 1) / 2;
	while (child>0&&a[child]>a[parent])
	{
		swap(&a[parent],&a[child]);
		child = parent;
		parent = (child - 1) / 2;
	}
}
//向上调整(小堆)
void AdjustUp(HPDataType* a, int n, int child)
{
	assert(a);
	int parent = (child - 1) / 2;
	while (child>0&&a[child]<a[parent])
	{
		swap(&a[parent],&a[child]);
		child = parent;
		parent = (child - 1) / 2;
	}
}

向下调整?

//向下调整(大堆)
void AdjustDown(HPDataType*a,int n,int root)
{
	assert(a);
	int parent = root;
	int child = root*2+1;
	if (child<n&&a[child+1] > a[child ])
	{
		child++;
	}
	while (child<n&&a[child]>a[parent])
	{
		swap(&a[child],&a[parent]);
		parent = child;
		child = 2 * parent + 1;
		if (child<n && a[child+1] > a[child ])
		{
			child++;
		}
	}
}
//向下调整(小堆)
void AdjustDown(HPDataType*a,int n,int root)
{
	assert(a);
	int parent = root;
	int child = root*2+1;
	if (child<n&&a[child+1] < a[child ])
	{
		child++;
	}
	while (child<n&&a[child]<a[parent])
	{
		swap(&a[child],&a[parent]);
		parent = child;
		child = 2 * parent + 1;
		if (child<n && a[child+1] <a[child ])
		{
			child++;
		}
	}
}

交换?

//交换
void swap(HPDataType*a, HPDataType*b)
{
	HPDataType tmp = *a;
	*a = *b;
	*b = tmp;
}

堆的应用

堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1. 建堆
升序:建大堆
降序:建小堆
2. 利用堆删除思想来进行排序
建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序。

?下面是借用大堆来实现排升序的代码

//建大堆排升序
void HeapSort(int *a,int n)
{
	//先建大堆
	for (int i = (n - 2) / 2; i >= 0; i--)
	{
		AdjustDown(a , n, i);//大堆
	}
	//再排序
	for (int i = n-1; i > 0; i--)
	{
		swap(&a[0], &a[i]);
		AdjustDown(a, i, 0);//大堆
	}
}

TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能
数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:
1. 用数据集合中前K个元素来建堆
前k个最大的元素,则建小堆
前k个最小的元素,则建大堆
2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。
?

//建小堆来取最大前K个数
void PrintTopK(int* a, int n, int k)
{
	assert(a);
	Heap hp;
// 创建一个K个数的小堆
	HeapInit(&hp,a,k);
	for (int i=k;i<n;i++)
	{
		if (HeapTop(&hp) < a[i])
		{
			HeapPop(&hp);
			HeapPush(&hp, a[i]);
		}
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", HeapTop(&hp));
		HeapPop(&hp);
	}
}
//测试
void TestTopk()
{
int n = 10000;
int* a = (int*)malloc(sizeof(int)*n);
srand(time(0));
for (size_t i = 0; i < n; ++i)
{
a[i] = rand() % 1000000;
}
a[5] = 1000000 + 1;
a[1231] = 1000000 + 2;
a[531] = 1000000 + 3;
a[5121] = 1000000 + 4;
a[115] = 1000000 + 5;
a[2335] = 1000000 + 6;
a[9999] = 1000000 + 7;
a[76] = 1000000 + 8;
a[423] = 1000000 + 9;
a[3144] = 1000000 + 10;
PrintTopK( a, n, 10);
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-11-23 12:37:09  更:2021-11-23 12:37:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/9 16:14:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码