一、选择排序算法的实现
1. 基本思想
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的 数据元素排完 。
二、排序过程
1. 动图分析
2. 实例分析
定义一个数组 int a[4] = [3,2,8,4],要求利用选择排序的方法将数组从小到大排序。
第一趟排序:从4个元素里面找到最小的,与第一个元素进行交换,将最小元素存放在起始位置
排序后为:2 ,3,8,4 |
第二趟排序:从剩下的3个元素中找最小的,与待排序的第一个元素(3)进行交换,将最小元素存放在第二个位置。可以发现最小的元素就是3
排序后为:2 ,3,8,4 |
第三趟排序:从剩下的2个元素里面找最小的,与待排序的第一个元素(8)进行交换,将最小元素存放在第三个位置
排序后为:2 ,3,4,8 |
第四趟排序: 其实这一趟不用排序了,因为前面已经筛选好了,最后一个数肯定是最大的了
排序后为:2 ,3,4,8 |
3. 代码解释
void SelectSort(int* a, int n)
{
int i = 0;
for (i = 0; i < n-1; i++) //i代表参与该趟选择排序的第一个元素的下标
{
int start = i; // 初始化start为当前轮次的起始位置i
int min = start; // 最小值索引初始化为start
while (start < n)
{
// 从当前位置i开始,向右遍历至数组末尾
if (a[start] < a[min])
{
min = start; // 如果发现更小的值,则更新最小值索引
}
start++; // 移动到下一个元素
}
Swap(&a[i], &a[min]); // 与当前位置i处的元素交换,确保最小元素位于其正确的位置
}
}
三选择排序的优化方案
采用双向选择排序:传统的选择排序每次只确定了一个位置(最小值),而双向选择排序则同时确定两个位置——最小值和最大值。每一趟排序中,同时查找当前未排序部分的最小值和最大值,将最小值放在未排序部分的开始,最大值放在末尾。这样可以减少排序所需的总趟数,从而在一定程度上提高效率。
1. 实例分析
假设我们有一个未排序的数组:arr [10,29,14,37,13],运用双向选择进行排序
第一趟排序:
- 初始化left = 0,right = 4;
- 寻找从 left 到 right 的最小值和最大值:
- 最小值为 arr[1] = 10,位置在索引 1
- 最大值为 arr[3] = 37,位置在索引 3
- 交换最小值
10
和左边界值29
,得到 [10,29,14,37,13] - 交换最大值
37
和右边界值13
,得到 [10,29,14,13,37] - 缩小范围:left = 1,right = 3
第二趟排序
- 继续排序,left = 1,right = 3
- 寻找从 left 到 right 的最小值和最大值:
- 最小值为
13
,位置在索引 3 - 最大值为
29
,位置在索引 1
- 最小值为
- 交换最小值
13
和左边界值29
,得到 [10,13,14,29,37] - 由于最大值已经是
29
且在正确的位置(经由最小值交换到位),[10,13,14,29,37] - 缩小范围:left = 2,right = 2
- 此时left = right,说明14的位置也是正确的,即[10,13,14,29,37]
结束条件
- 当 left >= right时,排序完成
2. 代码解释
void SelectSort(int* a, int n)
{
int left = 0; // left指针,指向当前未排序部分的开始
int right = n - 1; // right指针,指向当前未排序部分的结束
while (left < right) // 当左指针小于右指针时,继续排序
{
int minIndex = left; // 初始化最小值索引为当前左边界
int maxIndex = left; // 初始化最大值索引为当前左边界
int i = 0;
// 遍历当前未排序的部分,从left到right
for (i = left; i <= right; i++)
{
if (a[i] < a[minIndex]) // 如果当前元素小于已知的最小值
minIndex = i; // 更新最小值索引
if (a[i] > a[maxIndex]) // 如果当前元素大于已知的最大值
maxIndex = i; // 更新最大值索引
}
// 交换最小值到当前未排序部分的开始位置
Swap(&a[minIndex], &a[left]);
// 如果最大值在最小值的初始位置,更新最大值索引
if (left == maxIndex)
{
maxIndex = minIndex; // 因为最小值已经被交换到left位置
}
// 交换最大值到当前未排序部分的结束位置
Swap(&a[maxIndex], &a[right]);
// 缩小未排序的范围
left++; // 移动左边界向右
right--; // 移动右边界向左
}
}
四、选择排序的时间复杂度和空间复杂度
时间复杂度
选择排序的时间复杂度在所有情况下都是一样的,因为它总是执行完整的比较过程,无论输入数据的初始状态如何。
最好情况:O(n²):
- 即使数组已经是排序好的,选择排序仍然会执行 𝑛−1n−1 轮比较来确认每个元素已经处于其正确的位置。
最坏情况:O(n²):
- 当数组是反序时,每一轮选择中找最小元素的过程也是需要完整的比较。
平均情况:O(n²):
- 对于随机顺序的输入数组,选择排序的平均性能和最好、最坏情况相同,因为它始终执行完全的比较和交换过程。
选择排序的这种性能表现源于它的选择机制:每次都要遍历剩余未排序的部分来找到最小元素,这个过程不受输入数据状态的影响。
空间复杂度
选择排序是一个原地排序算法
空间复杂度:O(1)
- 选择排序不需要额外的存储空间,因为它通过交换操作在原数组上进行排序。它只需要一个临时变量来存储每次找到的最小(或最大)元素的索引。
总结
选择排序因其简单和原地排序的特性,在小规模数据或内存使用受限的情况下还是有一定用途的。但由于其较差的时间效率,它通常不适用于处理大量数据。在需要处理大数据集时,更高效的排序算法如快速排序、归并排序或堆排序更为适宜。