首页 > 编程语言 >[排序算法]选择排序+堆排序全梳理!

[排序算法]选择排序+堆排序全梳理!

时间:2024-06-01 17:57:26浏览次数:21  
标签:结点 int 复杂度 元素 堆排序 算法 排序

目录

前言

今天我们将学习排序算法中的 直接选择排序堆排序,它们的基本思想都是 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。 所以这两个可以统称为 选择排序

1. 直接选择排序

基本思想

选择排序(Selection-Sort)是一种简单直观的排序算法。

它的基本思想:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

生活中从矮到高排队就是就是运用这种思想。
举个例子:
大家上体育课时,老师都会让大家从矮到高排队,假设有5名同学.

首先老师找到了个子最矮的 5 号同学,然后老师说:5 号同学,你是最矮的,跟 1 号交换一下位置!

在这里插入图片描述
这时候,老师又说:4 号同学,你是第二矮的,跟 2 号交换一下位置!
在这里插入图片描述
老师又说:2 号同学,你是第三矮的,你和 3 号交换一下位置!
在这里插入图片描述
最后,老师说:1 号同学,你是第四矮的,你和 3 号交换一下位置吧!
在这里插入图片描述
如此一来,每一轮选出最小者直接交换到左侧的思路,就是选择排序的思路。这种排序的最大优势就是省去了多余的元素交换。
在这里插入图片描述

具体步骤:

(1)在元素集合 array[i] 到 array[n-1] 中选择关键码最大(小)的数据元素。

(2)若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换。

(3)在剩余的 array[i] 到 array[n-2](array[i+1] 到 array[n-1])集合中,重复上述步骤,直到集合剩余 1 个元素。

动图演示

我们来看看选择排序的动图演示吧。
在这里插入图片描述

代码实现

实际上,我们可以一趟选出两个值,一个最大值和一个最小值,然后将其放在序列开头和末尾,这样可以使选择排序的效率快一倍。

//交换函数
void Swap(int* pa, int* pb) {
	int tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}

//直接选择排序(优化版本)
void SelectSort(int* a, int n) {
	int left = 0;
	int right = n - 1;
	while (left < right) {
		int mini = left;
		int maxi = left;
		//遍历区间:[left+1, right]
		//选出最小的和最大的,然后交换
		for (int i = left + 1; i <= right; ++i) {
			//选出最小的数
			if (a[i] < a[mini]) {
				mini = i;
			}
			//选出最大的数
			if (a[i] > a[maxi]) {
				maxi = i;
			}
		}
		Swap(&a[left], &a[mini]); //把最小的数放在最左边
		//如果left和maxi重叠,修正一下maxi即可
		if (left == maxi) {
			maxi = mini;
		}
		Swap(&a[right], &a[maxi]); //把最大的数放在最右边
		left++;
		right--;
	}
}

直接选择特性总结:

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用.

  2. 时间复杂度:O(N^2)

  3. 空间复杂度:O(1)

  4. 稳定性:不稳定

2. 堆排序

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

回顾一下:
堆是具有以下性质的完全二叉树:

(1)每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;

(2)每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。

如下图所示,就是两种堆的类型:

在这里插入图片描述

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子:

在这里插入图片描述

向下调整算法

既然要进行堆排序,那么就要建堆,而建堆的方式有两种:

1)使用向上调整,插入数据的思想建堆,但是时间复杂度为:O ( N ∗ l o g N )

2)使用向下调整,插入数据的思想建堆,时间复杂度为:O ( N )

所以我们这里推荐使用 堆的向下调整算法,那么建堆也是有 2 个前提的:

(1)如果需要从⼤到⼩排序,就要将其调整为小堆,那么根结点的左右子树必须都为小堆。

(2)如果需要从⼩到⼤排序,就要将其调整为大堆,那么根结点的左右子树必须都为大堆。

但是我们先了解一下什么叫做大堆,如下图:
在这里插入图片描述

注意:有一个概念别搞错了,调整大堆并不是把元素从大到小排列,而是每个根节点都比它的叶子节点大

向下调整建大堆算法的基本思想:

(1)从根结点处开始,选出左右孩子中值较大的孩子,让大的孩子与其父亲进行比较。

(2)若大的孩子比父亲还大,则该孩子与其父亲的位置进行交换。并将原来大的孩子的位置当成父亲继续向下进行调整,直到调整到叶子结点为止。

(3)若大的孩子比父亲小,则不需处理了,调整完成,整个树已经是大堆了。

如下图所示:
在这里插入图片描述

堆的向下调整算法代码

//向下调整大堆
void AdjustDownBig(HPDataType* a, size_t size, size_t root) {
	size_t parent = root;
	size_t child = 2 * parent + 1; //默认左孩子最大

	while (child < size)
	{
		//1.找出左右孩子中大的那个
		//如果右孩子存在,且右孩子小于size(元素个数),那么就把默认小的左孩子修改为右孩子
		if (child + 1 < size && a[child + 1] > a[child]) {
			++child;
		}

		//2.把大的孩子去和父亲比较,如果比父亲大,就交换
		if (a[child] > a[parent]) {
			Swap(&a[child], &a[parent]);
			parent = child;
			child = 2 * parent + 1;
		}
		else {
			break; //如果孩子小于等于父亲,那么直接跳出循环
		}
	}
}

使用堆的向下调整算法,最坏的情况下(即一直需要交换结点),需要循环的次数为:h − 1 次( h 为树的高度)。

而 h = log2(N+1)( N 为树的总结点数)。

所以堆的向下调整算法的时间复杂度为:O(logN) 。

任意树调整为堆的思想

上面说到,使用堆的向下调整算法需要满足其根结点的左右子树均为大堆或是小堆才行,那么如何才能将一个任意树调整为堆呢?

答案很简单,我们只需要从 倒数第一个非叶子结点 开始,从后往前,按下标,依次作为根去向下调整即可。

注意:倒数第一个非叶子结点,即为最后一个节点的父亲,也被叫做根。

如下图所示:

在这里插入图片描述

建堆代码:

//从倒数第一个非叶子节点开始(最后一个节点的父亲)
//n-1是最后一个节点的下标,(n-1-1)/2最后一个节点的父亲的下标

for (int i = (n - 1 - 1) / 2; i >= 0; --i) {
	AdjustDownBig(a, n, i);
}

那么建堆的时间复杂度又是多少呢?

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

在这里插入图片描述
因此:建堆的时间复杂度为O(N)。

总结一下:

堆的向下调整算法的时间复杂度:O ( l o g N )

建堆的时间复杂度:O ( N )

堆排序

堆排序的基本思想:

1)将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;

2)将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

3)重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

动图演示

我们来看堆排序的动图过程:
在这里插入图片描述

void HeapSort(int* a, int n) 
{
	//升序 --> 建大堆
	//建堆:使用向下调整 --> O(N)
	//从倒数第一个非叶子节点开始(最后一个节点的父亲)
	//n-1是最后一个节点的下标,(n-1-1)/2最后一个节点的父亲的下标
		
	for (int i = (n - 1 - 1) / 2; i >= 0; --i) 
	{
		AdjustDownBig(a, n, i);
	}


	size_t end = n - 1; //最后一个元素的下标
	while (end > 0)
	{
		Swap(&a[0], &a[end]); //交换第一个元素和最后一个元素
		AdjustDownBig(a, end, 0);
		--end;
	}
}

选择排序的特性总结:

堆排序是一种选择排序,整体主要由 构建初始堆 + 交换堆顶元素和末尾元素并重建堆 两部分组成。

1.堆排序使用堆来选数,效率就高了很多。

2.时间复杂度:O(N*logN)

3.空间复杂度:O(1)

4.稳定性:不稳定

3.总结

在这里插入图片描述

标签:结点,int,复杂度,元素,堆排序,算法,排序
From: https://blog.csdn.net/f_2789889770/article/details/139346348

相关文章

  • 算法随笔——数论之莫比乌斯反演
    链接链接2链接3链接4前置知识:数论分块可以求形如:\(\sumf(i)g(\left\lfloorn/i\right\rfloor)\)的东西。原理如下:比如说求$\sum_{i=1}^{10}\left\lfloor10/i\right\rfloor$得到:10532211111可以发现有一些块的数值是一样的。具体一点可以发现\([l......
  • 冒泡排序
    //通过指针交换两个元素的值voidswap(int*a,int*b){inttemp=*a;*a=*b;*b=temp;}/*****************************************************************name;BubbleSort*function:sort*parameter;*@intarr[]*......
  • 插入排序
    //通过指针交换两个元素的值voidswap(int*a,int*b){inttemp=*a;*a=*b;*b=temp;}/*****************************************************************name;InsertSort*function:sort*parameter;*@intarr[]*......
  • 回溯算法详解
    回溯回溯概念题解组合问题LeetCode-77组合题目描述:题目思路:代码LeetCode-216组合Ⅲ题目描述题目思路代码LeetCode-39组合总数题目描述:解题思路代码排列问题LeetCode-46全排列题目描述解题思路代码回溯概念题解组合问题LeetCode-77组合LeetCode-77组......
  • 机器学习_回归算法详解
    机器学习中的回归算法用于预测连续数值输出(目标变量),通过学习输入特征(自变量)与目标变量之间的关系。以下详细介绍几种常见的回归算法及其工作原理,并提供相应的代码示例。1.线性回归(LinearRegression)1.1简介线性回归是最简单、最常用的回归算法之一,假设目标变量(y)......
  • python 卡尔曼滤波算法
    卡尔曼滤波(KalmanFilter)是一种有效的递归滤波器,用于线性动态系统的状态估计。它通过考虑先前的估计和当前的观测来提供下一个状态的最佳估计。卡尔曼滤波器广泛应用于导航系统、机器人定位、信号处理等领域。下面是一个简单的Python实现卡尔曼滤波算法的例子,用于估计一个一维......
  • 基于Matlab多算法去雾系统
    欢迎大家点赞、收藏、关注、评论啦,由于篇幅有限,只展示了部分核心代码。文章目录一项目简介二、功能三、系统四.总结一项目简介  一、项目背景与意义在图像处理和计算机视觉领域,图像去雾是一个重要的研究方向。由于雾天或其他恶劣天气条件,户外图像往往会出......
  • FPGA图像处理--CLAHE算法(一)
    FPGA交流群:838607138本文首发于公众号:FPGA开源工坊在介绍CLAHE算法之前必须要先提一下直方图均衡化,直方图均衡化算法是一种常见的图像增强算法,可以让像素的亮度分配的更加均匀从而获得一个比较好的观察效果。如下图就是经过直方图均衡化后的效果图。importcv2importnumpya......
  • 【计算机毕业设计】谷物识别系统Python+人工智能深度学习+TensorFlow+卷积算法网络模
    谷物识别系统,本系统使用Python作为主要编程语言,通过TensorFlow搭建ResNet50卷积神经算法网络模型,通过对11种谷物图片数据集('大米','小米','燕麦','玉米渣','红豆','绿豆','花生仁','荞麦','黄豆','黑米','黑豆')进行训练......
  • 操作系统之CPU调度算法——FCFS、SJF和SRTF
    目录前言 FCFS先来先服务调度算法定义与步骤 举例SJF短作业优先调度算法定义与步骤举例SRTF最短剩余时间优先调度算法定义与步骤举例结束语​​​​​​​前言 今天是坚持写博客的第12天,为不断坚持的自己和大家点赞。最近经历了一场时长半小时的答辩,还是需......