📌 博客主页 爆打维c
本文将介绍十大经典排序算法之选择排序
废话不多说,我们开始吧!

一、选择排序的原理
从头至尾扫描序列,找出最小的一个元素,和第一个元素交换,接着从剩下的元素中继续这种选择和交换方式,最终得到一个有序序列。
开始:
1. 设定一个未排序的数组。
2. 设定初始索引 i = 0。
外层循环:
3. 如果 i < 数组长度 - 1,则执行以下步骤,否则结束。
内层循环:
4. 设定最小值索引 flag = i。
5. 设定 j = i + 1。
6. 如果 j < 数组长度,则执行以下步骤,否则跳到步骤 9。
7. 如果 array[j] < array[flag],则将flag 更新为 j。
8. 增加 j 的值,然后跳到步骤 6。
9. 交换 array[i] 和 array[flag] 的值。
10. 增加 i 的值,然后跳到步骤 3。
1.遍历第一趟数组,找到最小的那个值,若不是第一个元素则交换二者位置。

此时min指向第一个元素,不需要更换
2.遍历第二趟,继续找到当前数组里最小的那个值(已经排好的不需要再找)

此时min指向的下标不是处于数组最左端的元素,则交换位置
arr 1 ,12,33,21,123,22
3.继续遍历,重复上述步骤,最后得到有序数组

二、选择排序的优化
选择排序本身的时间复杂度为O(n^2),在大规模数据的情况下效率较低。
可以通过以下两种方式对选择排序进行优化:
1.最小值和最大值同时找:常规的选择排序是每次找到未排序部分的最小值并进行交换。而优化的方式是同时找到未排序部分的最小值和最大值,然后将最小值放在未排序部分的开头,将最大值放在未排序部分的末尾。这样可以减少了一半的比较和交换次数,从而提高了效率。
2.跳过有序部分的比较:在每次找到最小值后,将最小值放在合适位置之后,已经有序的部分就增加了一个元素。在下一轮的比较中,可以跳过这个已经有序的部分,只对未排序部分进行比较,从而减少了比较次数。
这种优化方式能够减少选择排序的比较和交换次数,从而提高算法的效率。
开始:
1. 设定一个未排序的数组。
2. 设定初始索引 i = 0。
3. 设定初始索引 j = 数组长度 - 1。
外层循环:
4. 如果 i < j,则执行以下步骤,否则结束。
内层循环:
5. 设定最小值索引 min_index = i。
6. 设定最大值索引 max_index = j。
7. 设定 k = i + 1。
8. 如果 k <= j,则执行以下步骤,否则跳到步骤 14。
9. 如果 array[k] < array[min_index],则将 min_index 更新为 k。
10. 如果 array[k] > array[max_index],则将 max_index 更新为 k。
11. 增加 k 的值,然后跳到步骤 8。
12. 交换 array[i] 和 array[min_index] 的值,如果 i 等于 max_index 则将 max_index 更新为 min_index。
13. 交换 array[j] 和 array[max_index] 的值。
14. 增加 i 的值,减小 j 的值,然后跳到步骤 4。
结束。
下面给出优化前后及递归版本的代码
此版本是根据最小值最大值同时找的方法进行优化,代码注释已经给出,便于读者参考
#include<stdio.h>
void Swap(int* p1, int* p2) {
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void Selection1(int* arr, int n) {
int i, j;
int flag = 0;
for (i = 0; i <n-1; i++) {
flag = i;
for (j = i + 1; j < n;j++) {
if (arr[flag] > arr[j]) {
flag = j; //记下该元素的位置
}
//如果flag不是原来的位置,那么交换两个数的值
if (flag != i) { Swap(&arr[flag], &arr[i]); }
}
//flag++;
}
}
void Selection2(int* arr, int n) {
int left = 0, right = n - 1;
int max,min;//分别记录最大值与最小值
while (left < right) {
max = min = left;
int i = 0;
for (i=left; i <=right; i++) {
if (arr[i] > arr[max]) {
max = i; //记录最大值的位置
}
if (arr[i] < arr[min]) {
min = i; //记录最小值的位置
}
}
if (max != right) { //最大值不是在最右边的话则交换值
Swap(&arr[max], &arr[right]);
}
if (min == right) { //如果最小值在最右边的位置 上述操作已经将最大值放到最右边了
min = max; //所以要将最小值的位置放到最大值的位置上
// Swap(&arr[min], &arr[left]);
}
if (min != left) {
Swap(&arr[min], &arr[left]);
}
left++;
right--;
}
}
//递归版1
void Selection3(int*arr,int n){
if (n < 2) return; //元素小于2个不需要排序
int i=0;
int pos = 0;
for (i = 1; i < n; i++) {
if (arr[i] < arr[pos]) {
pos = i; //记录最小值的位置
}
//如果本趟循环的最小的元素不是起始位置的元素,则交换它的位置到最左边(0下标处)。
if (pos != 0) {
Swap(&arr[pos], &arr[0]);
}
}
Selection3(arr+1,--n);
}
//优化递归版2
void Selection4(int* arr, int n) {
if (n < 2) return; //元素小于2个不需要排序
int i = 0;
int maxpos, minpos;
int left = 0, right = n - 1;
for (i = left; i <= right;i++) { //循环从left和right之间选取元素
maxpos = minpos = left;
int i = 0;
for (i = left; i <= right; i++) {
if (arr[i] > arr[maxpos]) {
maxpos = i; //记录最大值的位置
}
if (arr[i] < arr[minpos]) {
minpos = i; //记录最小值的位置
}
}
if (maxpos != right) { //最大值不是在最右边的话则交换值
Swap(&arr[maxpos], &arr[right]);
}
if (minpos == right) { //如果最小值在最右边的位置 上述操作已经将最大值放到最右边了
minpos = maxpos; //所以要将最小值的位置放到最大值的位置上
// Swap(&arr[min], &arr[left]);
}
if (minpos != left) {
Swap(&arr[minpos], &arr[left]);
}
}
n = n - 2;
Selection4(arr + 1, n);
}
int main(){
int arr[] = { 1,22,33,21,123,12 };
int sz = sizeof(arr) / sizeof(arr[0]);
Selection1(arr, sz);
int i;
for (i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
return 0;
}
本文详细介绍了选择排序的基本原理,包括其工作流程,并探讨了如何通过同时寻找最小值和最大值以及跳过有序部分进行优化,以提高排序效率。





