

選擇排序
選擇排序也是利用了“擋板法”這個經典思想,
擋板左邊是已排序區間,右邊是未排序區間,那么每次的“選擇”是去找右邊未排序區間的最小值,找到之后和擋板后面的第一個值換一下,然后再把擋板往右移動一位,保證排好序的這些元素在擋板的左邊,
比如之前的例子:{5, 2, 0, 1}
我們用一個擋板來分隔陣列是否排好序, 用指標 j 來尋找未排序區間的最小值;

第一輪 j 最初指向 5,然后遍歷整個未排序區間,最終指向 0,那么 0 就和擋板后的第一個元素換一下,也就是和 5 交換一下位置,擋板向右移動一位,結束第一輪,

第二輪,j 從擋板后的2開始遍歷,最終指向1,然后1和擋板后的第一個元素 2 換一下,擋板向右移動一位,結束第二輪,

第三輪,j 從2開始遍歷,最終指向2,然后和2自己換一下,擋板向右移動一位,結束第三輪,

還剩一個元素,不用遍歷了,就結束了,
選擇排序與之前的插入排序對比來看,要注意兩點:
擋板必須從 0 開始,而不能從 1 開始,雖然在這兩種演算法中,擋板的物理意義都是分隔已排序和未排序區間,但是它們的已排序區間里放的元素的意義不同:
選擇排序是只能把當前的最小值放進來,而不能放其他的; 插入排序的第一個元素可以為任意值,
所以選擇排序的擋板左邊最開始不能有任何元素,
在外層回圈時,
選擇排序的最后一輪可以省略,因為只剩下最大的那個元素了; 插入排序的最后一輪不可省略,因為它的位置還沒定呢,
class Solution {
public void selectionSort(int[] input) {
if(input == null || input.length <= 1) {
return;
}
for(int i = 0; i < input.length - 1; i++) {
int minValueIndex = i;
for(int j = i + 1; j < input.length; j++) {
if(input[j] < input[minValueIndex]) {
minValueIndex = j;
}
}
swap(input, minValueIndex, i);
}
}
private void swap(int[] input, int x, int y) {
int tmp = input[x];
input[x] = input[y];
input[y] = tmp;
}
}

時間復雜度
最內層的 if 陳述句每執行一次是 O(1) ,那么要執行多少次呢?
當 i = 0 時,是 n-1 次; 當 i = 1 時,是 n-2 次; ... 最后是 1 次;
所以加起來,總共是: (n-1) + (n-2) + … + 1 = n*(n-1) / 2 = O(n^2)
是這樣算出來的,而不是一拍腦袋說兩層回圈就是 O(n^2).
空間復雜度
這個很簡單,最多的情況是 call swap() 的時候,然后 call stack 上每一層就用了幾個有限的變數,所以是 O(1),
那自然也是原地排序演算法了,
穩定性
這個答案是否定的,選擇排序并沒有穩定性,
因為交換的程序破壞了原有的相對順序,比如: {5, 5, 2, 1, 0} 這個例子,第一次交換是 0 和 第一個 5 交換,于是第一個 5 跑到了陣列的最后一位,且再也無翻身之地,所以第一個 5 第二個 5 的相對順序就已經打亂了,
這個問題在石頭哥的那篇谷歌面經文章里有被考到哦,如果還沒有看過這篇面經文章的,在公眾號里回復「谷歌」二字,就可以看到了,
優化
選擇排序的其中一步是選出每一輪的最小值,那么這一步如果使用 heapify() 來優化,就可以從 O(n) 優化到 O(logn),這其實就變成了 heapSort.



如果你喜歡這篇文章,記得給我點贊留言哦~你們的支持和認可,就是我創作的最大動力,我們下篇文章見!
我是小齊,紐約程式媛,終生學習者,每天晚上 9 點,云自習室里不見不散!
更多干貨文章見我的 Github: https://github.com/xiaoqi6666/NYCSDE
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/183616.html
標籤:其他
