【排序】选择排序(含优化版)

本章我们继续讲排序算法,这里我们将学习选择排序,也是一个很普遍很常见的排序算法,逻辑和代码都比较简单,比较容易掌握,我们直接走起

选择排序

基本思想:选择排序(SelectSort),即每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完

我们这里以排升序为例

选择排序与冒泡排序一样,也分为内层循环和外层循环,内层循环是找出最小的元素放到未排序数组的第一位,外层循环按照内层逻辑,一步一步的将元素由小到大依次放到数组内,完成排序

单趟逻辑 :单趟排序就是从记录本次循环的第一个元素,依次向后比较,若后面的元素大于记录的元素,那么就将记录的元素换为更小的元素,并记录此时记录元素的下标方便在出循环后进行交换,找到数组末尾,本次单趟循环结束,此时记录的元素就是最小元素,那么我们就进行交换

**整体逻辑:**沿用单趟逻辑,多次进行排序,若有n个数据,那么就需要外层循环n-1次,将数据全部排好。

逻辑图

代码实现

void SelectSort(int* a, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int tmp = a[i];
		int k = i;
		for (int j = i + 1; j < n; j++)
		{
			if (a[j] < tmp)
			{
				tmp = a[j];
				k = j;
			}
		}
		Swap(&a[i], &a[k]);
	}
}

优化

思路:上面的选择排序是基本的选择排序,我们可以发现,他每次循环都需要遍历未排序的数组去寻找最小的元素,那么我们换一种思路,如果我们在遍历未排序的数组时,同时找到最大的元素和最小的元素,是不是就减少了一半的循环量,思路有了,我们就直接开始实现

总体的思路还是和上面大差不差,但是这次我们需要多建一个变量end,来记录数组末尾的位置

然后依次进行遍历,之前我们用tmp记录最小的数,这里就需要用两个变量max 和 min来分别记录最大值和最小值,为了方便我们直接记录下标,这样就不用新建变量来记录下标了,而且还可以直接进行比较

流程图
优化代码(有瑕疵)
void SelectSort(int* a, int n)
{
	int begin = 0;
	int end = n - 1;
	while (begin < end)
	{
		int min = begin;
		int max = begin;
		for (int i = begin + 1; i <= end; i++)
		{
			if (a[i] > a[max])
			{
				max = i;
			}
			if (a[i] < a[min])
			{
				min = i;
			}
		}
			Swap(&a[begin], &a[min]);
			Swap(&a[end], &a[max]);
		
		begin++;
		end--;
	}
}

看了上面的分析和代码的实现,也许你会认为,这样就完成选择排序的优化,但是事实真的如此吗,先说结论,这个排序逻辑确实是这样的,代码基本也是对的,可以完成大多数的排序,但是有个别情况,是无法进行正确排序的,因此上面实现的是有瑕疵的

我们来分析一下,当第一轮排序时,我们上面的代码会记录

max为0,min为 1 ,begin为0,end为7

进行交换的时候,先交换begin和min

结果为 1 9 2 5 7 4 6 3

再交换max和end的时候

结果为 3 9 2 5 7 4 6 1

很显然,已经出错了,max在和end进行交换的时候,max已经变换了位置,因此我们找到了问题,找到了问题之后,我们就需要解决问题

我们只需要加一个判断条件,当最大值为begin时,第一步begin和min交换就会把max的值交换走,这时我们需要进行判断,如果begin==max,那么就将max的值赋为min,即此时最大值被交换到的位置,再进行第二步交换即可。

优化代码最终实现
void SelectSort(int* a, int n)
{
	int begin = 0, end = n - 1;

	while (begin < end)
	{
		int mini = begin, maxi = begin;
		for (int i = begin + 1; i <= end; ++i)
		{
			if (a[i] > a[maxi])
			{
				maxi = i;
			}

			if (a[i] < a[mini])
			{
				mini = i;
			}
		}

		Swap(&a[begin], &a[mini]);
		if (begin == maxi)
			maxi = mini;

		Swap(&a[end], &a[maxi]);
		++begin;
		--end;
	}
}

总结(冒泡排序和选择排序的区别)

冒泡排序的时间复杂度为O(n^2),其中n是要排序的元素数量。这是因为冒泡排序需要进行多次遍历,每次遍历都需要比较和交换元素。因此,对于大规模数据的排序,冒泡排序可能不是最有效的选择。

选择排序的时间复杂度也是O(n^2),但它的性能通常优于冒泡排序。因为选择排序只需要进行一次遍历,并且只需要进行一次比较和交换操作。因此,在处理大规模数据时,选择排序通常更为高效。

但如果遇到已经排好序的数据,冒泡排序只需要循环一次就能发现,并终止,但选择排序却需要继续遍历数组,在这一点上选择排序的效率是不如冒泡排序的。

相关推荐
网易独家音乐人Mike Zhou2 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
搬砖的小码农_Sky5 小时前
C语言:数组
c语言·数据结构
Swift社区6 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman6 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
先鱼鲨生6 小时前
数据结构——栈、队列
数据结构
一念之坤7 小时前
零基础学Python之数据结构 -- 01篇
数据结构·python
IT 青年7 小时前
数据结构 (1)基本概念和术语
数据结构·算法
熬夜学编程的小王7 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
Dong雨7 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展
SoraLuna7 小时前
「Mac玩转仓颉内测版24」基础篇4 - 浮点类型详解
开发语言·算法·macos·cangjie