选择排序
前面讲了插入排序,下面说另一种很容易想到的排序方法:选择排序。
原理
选择排序的原理非常简单,就像按学号点名,先点学号最小的,放在第一个位置,然后点学号第二小的,放在第二个位置,依此类推,直到剩最后一个为止。
实现
按照这个原理我们来用代码实现。这时问题又来了:是否可以像插入排序一样,也不需要另外开辟空间,直接在原址上排序呢?答案同样是可以。怎么做呢?因为已经排过序的和没有排过序的学生总数是一定的,我们将一个数组分成两部分,左边一部分表示已排序的学生,右边一部分表示还未排序的学生,这样一个数组就存储下了。
还有一个问题是点名选出来学生放在哪,只有一个数组意味着他必须要在未排序的学生这边占一个位置,当然他自己被选走了也会空出来一个位置,所以一个简单的解决办法就是做一个交换,另外一个办法是依次往右挪一个空位置出来。交换的方式运算量会小一些,但不稳定,而往右挪的方式可以保持稳定,但运算量大一些。(这里顺便解释一下稳定性:指的是当有任意两个或多个相同值的元素存在时,排序前后它们的相对位置是否能确定保持不变。比如数组中有两个2,为以示区别不妨分别叫2A和2B,排序前2A在2B的左边,若排序后不能保证2A还是在2B的左边,就是不稳定的排序。只要有任意一组相同元素排完序不稳定,排序算法就是不稳定的。必须保证所有位置所有元素排完序都稳定才能称之为稳定排序。比如插入排序就是一个稳定的排序。)
下面就是用C语言实现的代码,采用交换的方式。
- 要排序的数组a有n个元素,a[0...j-1]为已排好序的元素,a[j...n-1]是还未排序的。
- 外层循环进行n-1次,每次先从还未排序的元素a[j...n-1]中找到最小的一个a[index],然后交换a[index]与a[j]。
void selection_sort(int a[], int n)
{
if(n<=0)
return;
for(int j=0;j<n-1;j++) {
// 在还未排序的元素a[j...n-1]中找到最小的一个
int min=a[j];
int index=j;
for(int i=j;i<n;i++) {
if(a[i]<min) {
min=a[i];
index=i;
}
}
// 交换
a[index]=a[j];
a[j]=min;
}
}
为了验证此函数的效果,加上了如下辅助代码,对3个数组进行排序,运行结果在最后,可见排序成功。
#include <stdio.h>
#include <stdlib.h>
#define SIZE_ARRAY_1 5
#define SIZE_ARRAY_2 6
#define SIZE_ARRAY_3 20
void selection_sort(int a[], int n);
void show_array(int a[], int n);
void main()
{
int array1[SIZE_ARRAY_1]={1,4,2,-9,0};
int array2[SIZE_ARRAY_2]={10,5,2,1,9,2};
int array3[SIZE_ARRAY_3];
for(int i=0; i<SIZE_ARRAY_3; i++) {
array3[i] = (int)((40.0*rand())/(RAND_MAX+1.0)-20);
}
printf("Before sort, ");
show_array(array1, SIZE_ARRAY_1);
selection_sort(array1, SIZE_ARRAY_1);
printf("After sort, ");
show_array(array1, SIZE_ARRAY_1);
printf("Before sort, ");
show_array(array2, SIZE_ARRAY_2);
selection_sort(array2, SIZE_ARRAY_2);
printf("After sort, ");
show_array(array2, SIZE_ARRAY_2);
printf("Before sort, ");
show_array(array3, SIZE_ARRAY_3);
selection_sort(array3, SIZE_ARRAY_3);
printf("After sort, ");
show_array(array3, SIZE_ARRAY_3);
}
void show_array(int a[], int n)
{
if(n>0)
printf("This array has %d items: ", n);
else
printf("Error: array size should bigger than zero.\n");
for(int i=0; i<n; i++) {
printf("%d ", a[i]);
}
printf("\n");
}
运行结果:
Before sort, This array has 5 items: 1 4 2 -9 0
After sort, This array has 5 items: -9 0 1 2 4
Before sort, This array has 6 items: 10 5 2 1 9 2
After sort, This array has 6 items: 1 2 2 5 9 10
Before sort, This array has 20 items: 13 -4 11 11 16 -12 -6 10 -8 2 0 5 -5 0 18 16 5 8 -14 4
After sort, This array has 20 items: -14 -12 -8 -6 -5 -4 0 0 2 4 5 5 8 10 11 11 13 16 16 18
分析
时间复杂度
从代码可见,用了两层for循环,且每个循环都是 n 的量级,所以插入排序的时间复杂度为 O(n^2)。
空间复杂度
因为选择排序直接在原址进行,不需要另外的空间,所以空间复杂度是 O(1)。