[ 数据结构 -- 手撕排序算法第四篇 ] 选择排序

手撕排序算法系列之第四篇:选择排序。

从本篇文章开始,我会介绍并分析常见的几种排序,大致包括直接插入排序,冒泡排序,希尔排序,选择排序,堆排序,快速排序,归并排序等。

大家可以点击此链接阅读其他排序算法:排序算法_大合集(data-structure_Sort)

本篇主要来手撕选择排序~~  

目录

1.常见的排序算法 

1.1选择排序

2.直接选择排序

2.1基本思想(一次选一个数)

2.1.1选择排序动态视频  *(看视频更好理解) 

2.2基本思想(一次选两个数)

3.选择排序的实现

 3.1选择排序实现代码

4.选择排序测试

5.选择排序时间复杂度

6.选择排序的特性总结

1.常见的排序算法 

1.1选择排序

选择排序的基本思想:

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

 [ 数据结构 -- 手撕排序算法第四篇 ] 选择排序_第1张图片

2.直接选择排序

2.1基本思想(一次选一个数)

1、我们遍历一遍数组选出一个最小的数字。

2、让第一个数字和这个数字交换(默认升序排序),此时最小的数字就来到了第一个位置。

3、重复1操作选出第二小的数字,让第二个数字和这个次小的数字交换,此时最小的数字就来到了第二个位置.....循环选择排序,直至排序完成。

分析图解:

[ 数据结构 -- 手撕排序算法第四篇 ] 选择排序_第2张图片

2.1.1选择排序动态视频  *(看视频更好理解) 

选择排序

2.2基本思想(一次选两个数)

 这个方法每次只选择一个数字,显然有点大材小用。因为既然每次要选择数字,我们不妨每次选择2个数字。具体步骤如下:

1、我们遍历一遍数组选出一个最小的数字和一个最大的数字。

2、选择完成后让最小的数字放到第一个位置,最大的数字放到最后的位置。

3、当左边left大于等于右边right时说明排序完成

 [ 数据结构 -- 手撕排序算法第四篇 ] 选择排序_第3张图片

3.选择排序的实现

为了提高选择的效率,我们这里所选择的是每次选两个数,一个最大数一个最小数。这里我们需要有一个非常非常重要的细节需要把控。

如果有这么一种特殊的情况,在原数组中第一个位置的数字是整个数组最大的数字,那么按照我们之前的逻辑挪动还能得到我们想要的结果吗?结果是不能,具体分析看下图过程解析。

[ 数据结构 -- 手撕排序算法第四篇 ] 选择排序_第4张图片 

 

 因此为了避免此类问题造成结果出错,我们需要在程序中加一个修正,修正的逻辑如下:

如果最左边的left位置和max重叠,我们让min位置的值赋值给max位置,因为在第一次最小数字和left交换的时候,最大的数字被交换到了min位置。画图逻辑如下

[ 数据结构 -- 手撕排序算法第四篇 ] 选择排序_第5张图片

 3.1选择排序实现代码

//选择排序
void SelectSort(int* a, int n)
{
	int left = 0, right = n - 1;
	while (left < right)
	{
		int mini = left, maxi = left;
		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--;
	}
}

4.选择排序测试

//选择排序
void SelectSort(int* a, int n)
{
	int left = 0, right = n - 1;
	while (left < right)
	{
		int mini = left, maxi = left;
		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--;
	}
}
//打印数组
void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; i++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

//选择排序
void TestSelectSort()
{
	int a[] = { 9, 1, 2, 5, 7, 4, 8, 6, 3, 5 };
	SelectSort(a, sizeof(a) / sizeof(int));
	PrintArray(a, sizeof(a) / sizeof(int));
}

int main()
{
	//选择排序
	TestSelectSort();

	return 0;
}

测试结果:

[ 数据结构 -- 手撕排序算法第四篇 ] 选择排序_第6张图片

5.选择排序时间复杂度

选择排序的时间复杂度是O(n^2)。

无论是顺序还是逆序,选择排序的时间复杂度都是O(n^2),这是因为在每遍筛选数组时,都要遍历数组选出最大和最小的数组,每一次遍历即是O(n)。即使是自身有序的数组,但是计算机是不知道的,还是要遍历一遍选出最小和最大的。因此综上所述选择排序的时间复杂度是O(n^2)。 

6.选择排序的特性总结 

直接选择排序的特性总结:
1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
2. 时间复杂度: O(N^2)
3. 空间复杂度: O(1)
4. 稳定性:不稳定

(本篇完) 

你可能感兴趣的:(数据结构,排序算法,c语言,数据结构,排序算法)