📌 博客主页 爆打维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;
}