💓 博客主页:倔强的石头的CSDN主页
📝Gitee主页:倔强的石头的gitee主页
⏩ 文章专栏:《数据结构与算法》
期待您的关注
目录
一、引言
在数据处理的广阔领域中,排序算法作为基石般的存在,扮演着至关重要的角色。无论是数据库查询优化、数据分析、还是算法设计中的基础操作,排序都是不可或缺的一环。排序算法的性能直接影响到数据处理的整体效率,因此,研究和理解各种排序算法的原理、实现及其性能特点,对于提升数据处理能力具有重要意义。
在众多排序算法中,选择排序以其简洁直观的特点而著称。尽管在效率上不是最优的,特别是对于大规模数据集而言,但选择排序的算法思想却蕴含着深刻的逻辑和广泛的应用场景。通过不断选择剩余元素中的最小(或最大)元素,并将其放置到序列的起始位置,选择排序以一种简单而直接的方式完成了数据的排序。
通过本文的阅读,读者将能够掌握选择排序的核心思想,理解其实现原理,并学会在合适的场景下应用这一排序算法。同时,本文也将激发读者对排序算法更深入的探究兴趣,为进一步学习和研究更高效的排序算法打下基础。
二、选择排序的原理
选择排序(Selection Sort)是一种简单直观的排序算法。
它的工作原理是:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
三、实现思路
- 初始化:未排序区间为数组的全部元素,即整个数组;已排序区间为空。
- 遍历:从未排序区间中遍历找到最小(或最大)的元素。
- 交换:将找到的最小(或最大)的元素与未排序区间的第一个元素进行交换,该元素即为未排序区间的最小值(或最大值),因此交换后,它就到了已排序区间的末尾。
- 缩小未排序区间:将未排序区间的第一个元素排除(因为它已经是排序好的了),继续在剩余的未排序区间中重复上述步骤,直到未排序区间为空。
下图以升序排序为例进行演示
四、C语言实现代码
void swap(int* a, int* b)//交换函数
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void SelectSort1(int* a, int n)//选择排序降序
{
for (int i = 0; i < n - 1; i++)//控制选择次数
{
int mini = i;
for (int j = i + 1; j < n; j++)//控制查找范围
{
if (a[j] < a[mini])
mini = j;
}
swap(&a[i], &a[mini]);
}
}
void SelectSort2(int* a, int n)//选择排序降序
{
for (int i = 0; i < n - 1; i++)//控制选择次数
{
int maxi = i;
for (int j = i + 1; j < n; j++)//控制查找范围
{
if (a[j] > a[maxi])
maxi = j;
}
swap(&a[i], &a[maxi]);
}
}
五、性能分析
时间复杂度:O(n^2)
-
最好情况:即使数组已经是有序的,选择排序仍然需要遍历整个未排序部分以找到最小(或最大)元素。因此,最好情况下的时间复杂度仍然是O(n^2),其中n是数组的长度。
-
最坏情况:当数组完全逆序时,选择排序同样需要遍历整个未排序部分来找到最小(或最大)元素,并进行交换。这种情况下,时间复杂度同样为O(n^2)。
-
平均情况:在平均情况下,即数组中的元素随机分布时,选择排序每次遍历需要比较n-i-1次(其中n是数组长度,i是已排序的元素个数),总的时间复杂度仍然是O(n^2)。
综上所述,选择排序的时间复杂度为O(n^2),这意味着随着数据规模的增大,排序所需的时间将呈二次增长,效率较低。
空间复杂度:O(1)
选择排序的空间复杂度相对较低,为O(1)。这是因为选择排序在排序过程中只需要一个额外的空间来存储临时变量(用于交换元素),而不需要额外的数组或其他数据结构来存储数据。这使得选择排序在空间使用上非常高效。
稳定性:不稳定
选择排序是一种不稳定的排序算法。这是因为选择排序在每次遍历中都会找到剩余未排序部分的最小(或最大)元素,并将其与已排序部分的末尾元素交换位置。这种跨越式的交换可能会破坏原本相等元素的相对顺序,从而导致排序结果不稳定。
六、代码优化
选择排序每一次选择的过程都需要遍历一遍数组,找出最大值(最小值),这样的时间消耗无法避免。但可以在每一次遍历数组时,同时选出最大值和最小值,分别放在数组的左右两侧.
这样优化之后,时间消耗可以减少一半,但时间复杂度依然保持在O(N^2)的量级
需要注意的一点是:
最大值和最小值初始都以最前面的值开始向后进行比对,如果最大值就在初始位置,先将最小值交换之后,最大值就会被移走。
所以交换完最小值之后,就需要判断一下最大值是不是被交换走了,如果是的话,就要去最小值交换的位置找最大值
如下图所示
void swap(int* a, int* b)//交换函数
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void SelectSort(int* a, int n)//选择排序优化版——左右同时选择
{
int left = 0;
int right = n - 1;
while (left < right)//控制选择次数
{
int mini = left;
int 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[mini], &a[left]);
if (maxi == left)//如果最大值原本在left位置,mini的交换就将最大值换走了
maxi = mini;
swap(&a[maxi], &a[right]);
left++;
right--;
}
}
七、应用场景
选择排序以其简洁直观的特点而著称,但在性能上存在一定的局限性。其时间复杂度为O(n^2),在处理大规模数据集时效率较低;但其空间复杂度为O(1),且实现简单易懂,对于小规模数据或部分排序的情况仍然是一个不错的选择。
然而,在需要处理大规模数据或追求高效排序的场景中,可能需要考虑其他更高效的排序算法。
建议阅读:
与选择排序原理类似——每一次选择出最大值(最小值)的做法,但效率提升至O(n *log n)的堆排序
点击下方链接,知识+1+1+1……