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 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 《啊哈,算法》-18-数据结构-堆-神奇的优先队列 -> 正文阅读

[数据结构与算法]《啊哈,算法》-18-数据结构-堆-神奇的优先队列

一、什么是堆

1.堆是一种特殊的完全二叉树,就像下边这棵树一样:
在这里插入图片描述
2.发现:
我们发现这棵树的所有父节点都比子节点要小,符合这样的完全二叉树我们称为最小堆。
最大堆:如果二叉树的所有父节点都比子节点要大,就称为最大堆。

二、堆可以用来干什么

1.问题描述
加入有14个数,分别是99,5,36,7,22,17,46,12,2,19,25,28,1,92.
请找出这14个数中最小的数,请问应该怎么办呢?
最简单的方法就是将14个数从小扫描到尾,用一个循环就可以解决,但是这种方法的时间复杂度是O(14),也就是O(N)。
如果我们先删除最小的数,再需要增加一个新数23,并且再次求这14个数的最小数,那么这个时候时间复杂度就是O(N的平方)。

2.思路解析
上边的问题可以用堆很简单的解决。
首先我们将14个数按照最小堆的要求(就是所有的父节点都要比子节点小)放入一棵完全二叉树,就像下边的这棵树一样:
在这里插入图片描述
很显然最小的数就在堆顶,假设存储这个堆的数组叫做h的话,最小数就是h[1].
接下来我们将堆顶部的数删除,将新增加的23放在对顶,再次调节为最小堆。
如何调整为最小堆呢,向下调整,将23与两个儿子做对比,选择较小的一个与它交换,交换后如下:
在这里插入图片描述
我们发现此时还不符合最小堆的特性,继续讲下调整。
于是继续将23与它的两个儿子交换,结果如下:
在这里插入图片描述这样我们就实现了最小堆,知道了最小数。

3.代码复现
向下调整的代码如下:

void siftdown(int i)//传入一个需要向下调整的结点编号i,这里传入1,即从堆顶开始向下调整 
{
	int t,flag=0;//flag用来标记是否继续向下调整
	//当i结点有儿子(其实就是有左儿子的情况下)并且需要继续调整的时候,循环执行
	while( i*2<=n && flag==0) 
	{
		//首先判断它和左儿子的关系,并用t来记录值比较小的结点编号
		if( h[i]>h[i*2])
			t=i*2;
		else
			t=i;
		//如果有右儿子,再对右儿子进行讨论
		if( i*2+1<=n) 
		{
			//如果右儿子值更小,更新较小的结点编号
			if( h[t]>h[i*2+1]) 
				t=i*2+1;	
		}
		//如果发小最小的结点编号不是自己,说明子节点中有比父节点更小的
		if(t!=i) 
		{
			swap(t,i);//交换它们,注意swap函数要自己来写
			i=t;//更新i为刚才与它交换的儿子的结点,便于接下来继续向下调整。 
		}
		else
			flag=1;//否则说明当前的父节点已经比两个子节点都小了,不需要进行调整了 
	}
} 

三、堆排序

与快速排序一样,堆排序的时间复杂度也是O(NlogN)
.
如果我们现在要进行从小到大的排序,首先建立最小堆,然后每次删除顶部元素并将顶部元素输出或者放在一个新的数组中,直到堆空为止。最终输出的就是一个排序的序列。

1.建立最小堆,从小到大排序。

代码实现

//建立堆以及堆排序的完整代码
#include<stdio.h>
int h[101];//用来存放堆的数组
int n;//用来存储堆中元素的个数,也就是堆的大小

//交换函数,用来交换堆中两个元素的值
void swap(int x,int y)
{
	int t;
	t=h[x];
	h[x]=h[y];
	h[y]=t;
}

//向下调整函数
void siftdown(int i)//传入一个需要向下调整的结点编号i,这里传入1,即从堆顶开始向下调整 
{
	int t,flag=0;//flag用来标记是否继续向下调整
	//当i结点有儿子(其实就是有左儿子的情况下)并且需要继续调整的时候,循环执行
	while( i*2<=n && flag==0) 
	{
		//首先判断它和左儿子的关系,并用t来记录值比较小的结点编号
		if( h[i]>h[i*2])
			t=i*2;
		else
			t=i;
		//如果有右儿子,再对右儿子进行讨论
		if( i*2+1<=n) 
		{
			//如果右儿子值更小,更新较小的结点编号
			if( h[t]>h[i*2+1]) 
				t=i*2+1;	
		}
		//如果发小最小的结点编号不是自己,说明子节点中有比父节点更小的
		if(t!=i) 
		{
			swap(t,i);//交换它们,注意swap函数要自己来写
			i=t;//更新i为刚才与它交换的儿子的结点,便于接下来继续向下调整。 
		}
		else
			flag=1;//否则说明当前的父节点已经比两个子节点都小了,不需要进行调整了 
	}
} 

//建立堆的函数
void creat()
{
	int i;
	//从最后一个非叶节点到第1个节点依次进行向上调整 
	for(i=n/2;i>=1;i--)
	{
		siftdown(i);
	 } 
 } 
 
 
 
//删除最大的元素
int deletemax()
{
	int t;
	t=h[1];//用一个临时变量记录顶点的值
	h[1]=h[n];
	n--;
	siftdown(1);//向下调整
	return t;//返回之前记录的堆的顶点的最大值 
	
}
 
 
int main()
{
	int i,num;
	//读入要排序的数字的个数
	scanf("%d",&num);
	
	for(i=1;i<=num;i++)
	{
		scanf("%d",&h[i]);
	}
	n=num;
	
	//建堆
	creat();
	
	//删除顶部元素,连续删除n次,其实就是从小到大排列出来
	for(i=1;i<=num;i++) 
		printf("%d ",deletemax());
	
	getchar();
	getchar();
	return  0; 
	 
}

在这里插入图片描述

2.建立最大堆,从小到大排序。

代码实现

//从小到大排序,建立最大堆
#include<stdio.h>
int h[101];//用来存放堆的数组
int n;//用来存储堆中元素的个数,也就是堆的大小

//交换函数,用来交换堆中两个元素的值
void swap(int x,int y)
{
	int t;
	t=h[x];
	h[x]=h[y];
	h[y]=t;
}

//向下调整函数
void siftdown(int i)//传入一个需要向下调整的结点编号i,这里传入1,即从堆顶开始向下调整 
{
	int t,flag=0;//flag用来标记是否继续向下调整
	//当i结点有儿子(其实就是有左儿子的情况下)并且需要继续调整的时候,循环执行
	while( i*2<=n && flag==0) 
	{
		//首先判断它和左儿子的关系,并用t来记录值比较大的-结点编号
		if( h[i]<h[i*2])
			t=i*2;
		else
			t=i;
		//如果有右儿子,再对右儿子进行讨论
		if( i*2+1<=n) 
		{
			//如果右儿子值更大,更新较小的结点编号
			if( h[t]<h[i*2+1]) 
				t=i*2+1;	
		}
		//如果发小最大的结点编号不是自己,说明子节点中有比父节点更大的
		if(t!=i) 
		{
			swap(t,i);//交换它们,注意swap函数要自己来写
			i=t;//更新i为刚才与它交换的儿子的结点,便于接下来继续向下调整。 
		}
		else
			flag=1;//否则说明当前的父节点已经比两个子节点都小了,不需要进行调整了 
	}
} 

//建立堆的函数
void creat()
{
	int i;
	//从最后一个非叶节点到第1个节点依次进行向上调整 
	for(i=n/2;i>=1;i--)
	{
		siftdown(i);
	 } 
 } 
 
//堆排序
void heapsort()
{
	while(n>1)
	{
		swap(1,n);
		n--;
		siftdown(1);
	}
 } 
 
 
int main()
{
	int i,num;
	//读入要排序的数字的个数
	scanf("%d",&num);
	
	for(i=1;i<=num;i++)
	{
		scanf("%d",&h[i]);
	}
	n=num;
	
	//建堆
	creat();
	
	//堆排序 
	heapsort(); 
	
	//输出 
	for(i=1;i<=num;i++) 
		printf("%d ",h[i]);
	
	getchar();
	getchar();
	return  0; 
	 
} 

在这里插入图片描述

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

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 13:23:31-

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