【算法】选择排序
从算法逻辑上看,选择排序是一种简单直观的排序算法,在简单选择排序过程中,所需移动记录的次数比较少。
一、基本思想
选择排序的基本思想:比较 + 交换。
在未排序序列中找到最小(大)元素,存放到未排序序列的起始位置。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。
红色表示当前最小值,黄色表示已排序序列,绿色表示当前位置。
二、算法描述
- 从待排序序列中,找到关键字最小的元素;
- 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
- 从余下的
N - 1
个元素中,找出关键字最小的元素,重复1、2步,直到排序结束。
三、代码实现
选择排序比较简单,以下是我自己的实现,跟官方版差不多,所以完全可以参考。
/**
* 选择排序
*
* 1. 从待排序序列中,找到关键字最小的元素;
* 2. 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
* 3. 从余下的`N - 1`个元素中,找出关键字最小的元素,重复①、②步,直到排序结束。
* 仅增量因子为1时,整个序列作为一个表来处理,表长度即为整个序列的长度。
* @param arr 待排序数组
*/
public static int[] selectionSort(int[] array) {
//总共要经过N-1轮比较
for (int i = 0; i < array.length - 1; i++) {
int min = i;
//每轮需要比较的次数
for (int j = i + 1; j < array.length; j++) { //选出之后待排序中值最小的位置
if (array[j] < array[min]) {
min = j;//记录目前能找到的最小值元素的下标
}
}
//将找到的最小值和i位置所在的值进行交换
if (i != min) {
int temp = array[min]; //交换操作
array[min] = array[i];
array[i] = temp;
}
//第 i轮排序的结果为
System.out.print("第" + (i + 1) + "轮排序后的结果为:");
display(array);
}
return array;
}
//遍历显示数组
public static void display(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] array = {4, 2, 8, 9, 5, 7, 6, 1, 3};
//未排序数组顺序为
System.out.println("未排序数组顺序为:");
display(array);
System.out.println("-----------------------");
array = selectionSort(array);
System.out.println("-----------------------");
System.out.println("经过选择排序后的数组顺序为:");
display(array);
}
输出如下:
未排序数组顺序为:
4 2 8 9 5 7 6 1 3
-----------------------
第1轮排序后的结果为:1 2 8 9 5 7 6 4 3
第2轮排序后的结果为:1 2 8 9 5 7 6 4 3
第3轮排序后的结果为:1 2 3 9 5 7 6 4 8
第4轮排序后的结果为:1 2 3 4 5 7 6 9 8
第5轮排序后的结果为:1 2 3 4 5 7 6 9 8
第6轮排序后的结果为:1 2 3 4 5 6 7 9 8
第7轮排序后的结果为:1 2 3 4 5 6 7 9 8
第8轮排序后的结果为:1 2 3 4 5 6 7 8 9
-----------------------
经过选择排序后的数组顺序为:
1 2 3 4 5 6 7 8 9
四、性能分析
选择排序和冒泡排序执行了相同次数的比较:\(N *(N-1)/2\),但是至多只进行了N
次交换。
当N
值很大时,比较次数是主要的,所以和冒泡排序一样,用大O
表示是\(O(N^2)\)时间级别。但是由于选择排序交换的次数少,所以选择排序无疑是比冒泡排序快的。
当N
值较小时,如果交换时间比选择时间大的多,那么选择排序是相当快的。
以下是选择排序复杂度:
平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 |
---|---|---|---|
O(n²) | O(n²) | O(n²) | O(1) |
选择排序的简单和直观名副其实,这也造就了它”出了名的慢性子”,无论是哪种情况,哪怕原数组已排序完成,它也将花费将近n²/2
次遍历来确认一遍。即便是这样,它的排序结果也还是不稳定的。唯一值得高兴的是,它并不耗费额外的内存空间。