首先貼上從wiki上弄下來的關於選擇排序的定義。java
選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工做原理以下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。以此類推,直到全部元素均排序完畢。web
更加直白的解釋是,每次都從數組中選出最大或者最小的元素,而後放到數組的左邊。算法
老規矩,咱們仍是經過動圖來看一下選擇排序的過程。如下的gif來自於wiki。數組
而後咱們再經過我製做的gif,配上數據再瞭解一下過程。假設咱們的待排序數組仍是[5, 1, 3, 7, 6, 2, 4]。服務器
咱們使用Java來實現最多見的,選擇最小值的選擇排序,其代碼以下。微信
private void selectionSort(int[] arr) {
int min;
int minIndex;
for (int i = 0; i < arr.length - 1; i++) {
min = arr[i];
minIndex = -1;
for (int j = i; j < arr.length; j++) {
if (arr[j] < min) {
min = arr[j];
minIndex = j;
}
}
// 排序結束 交換位置
if (minIndex != -1) {
exchange(arr, i, minIndex);
}
}
}
private void exchange(int arr[], int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
int[] arr = new int[]{5, 1, 3, 7, 6, 2, 4};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7]
複製代碼
假設數組的長度爲7,那麼算法就須要進行6輪。若是數組的長度爲n,則算法須要進行n - 1輪。app
每一輪,算法都會從剩下的待排序元素中,選出最小的元素,並將其與當前數組下標爲i也就是有序序列的起始位置的元素交換。這樣一來,通過反覆的排序,最終造成有序數組。frontend
上面實現了選擇最小值的代碼,接下來咱們繼續實現選擇最大值的代碼。微服務
private void selectionSort(int[] arr) {
int max;
int maxIndex;
for (int i = 0; i < arr.length - 1; i++) {
max = Integer.MIN_VALUE;
maxIndex = -1;
for (int j = 0; j < arr.length - i; j++) {
if (max < arr[j]) {
max = arr[j];
maxIndex = j;
}
}
// 排序結束 交換位置
if (maxIndex != -1) {
exchange(arr, maxIndex, arr.length - i - 1);
}
}
}
private void exchange(int arr[], int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
int[] arr = new int[]{5, 1, 3, 7, 6, 2, 4};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7]
複製代碼
這個思想與選擇最小值的算法徹底同樣,只不過是選擇了最大值,每次都將剩餘序列的最大值放到數組的有序序列的最左邊。工具
那麼到此,選擇排序最多見的兩種寫法咱們都已經實現了。有的兄弟可能會想,這篇博客是否是結束了。其實咱們能夠從上面兩個算法中想到能夠優化的點。
既然咱們有兩個選擇,一種選擇最小值,另一種選擇最大值。那麼咱們爲何不一樣時進行兩個操做呢?
下面咱們就來實現這種算法。
private void selectionSort(int[] arr) {
int min;
int max;
int minIndex;
int maxIndex;
for (int i = 0; i <= arr.length / 2; i++) {
min = Integer.MAX_VALUE;
max = Integer.MIN_VALUE;
minIndex = -1;
maxIndex = -1;
for (int j = i; j < arr.length - i; j++) {
if (arr[j] < min) {
min = arr[j];
minIndex = j;
}
if (arr[j] > max) {
max = arr[j];
maxIndex = j;
}
}
// 排序結束 交換位置
if (minIndex != -1) {
if (maxIndex == i) {
maxIndex = minIndex;
}
exchange(arr, i, minIndex);
}
if (maxIndex != -1) {
exchange(arr, maxIndex, arr.length - 1 - i);
}
}
}
private void exchange(int arr[], int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
int[] arr = new int[]{5, 1, 3, 7, 6, 2, 4};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7]
複製代碼
由於選擇最大值和最小值同時進行,相對於上面兩種算法,同時選擇算法在執行次數上比前兩種算法減小了50%。
在運行時間上相對於選擇最小值和最大值分別減小了39.22%和62.20%。
如下是對同一個長度爲10000的隨機亂序數組使用三種算法的狀況。
[0 - 10000] 的亂序數組 | 取最小值 | 取最大值 | 同時取最大值最小值 |
---|---|---|---|
100次平均執行時間(ms) | 51 | 82 | 31 |
執行次數(次) | 50004999 | 50004999 | 25005000 |
最後咱們看一下選擇排序算法的時間複雜度。
其空間複雜度爲O(n),上面三種算法都屬於原地排序算法,除了交換元素使用了一個輔助空間以外,沒有額外申請空間,同時選擇排序是不穩定排序。
往期文章:
- 你知道和你不知道的冒泡排序
- 聊聊微服務集羣當中的自動化工具
- go源碼解析-Println的故事
- 用go-module做爲包管理器搭建go的web服務器
- WebAssembly徹底入門——瞭解wasm的前世今身
- 小強開飯店-從單體應用到微服務
相關:
- 微信公衆號: SH的全棧筆記(或直接在添加公衆號界面搜索微信號LunhaoHu)