本系列的文章列表和相關說明,請檢視序言。具體列表如下:
【一起學習排序演算法】0 序言
【一起學習排序演算法】1 演算法特性及大O記法
【一起學習排序演算法】2 氣泡排序
也可以直接到github上檢視完整的文章和原始碼!
本篇為此係列第三篇。
選擇排序 Selection sort
原理
先看看Wikipedia的定義:
The Selection sort algorithm divides the input list into two parts: the sublist of items already sorted and the sublist of items remaining to be sorted that occupy the rest of the list. Initially, the sorted sublist is empty and the unsorted sublist is the entire input list. The algorithm proceeds by finding the smallest element in the unsorted sublist, exchanging it with the leftmost unsorted element, and moving the sublist boundaries one element to the right. .
所以選擇排序的思路就是:
- 把列表分為兩個部分,一部分是已經排好序,一部分待排序。
- 初始有序子列為空,然後遍歷待排序子列,找出最小的元素,然後和待排序子列的第一個元素互換。然後遊標右移一個。這樣有序子列增加一個元素。
- 重複以上步驟,直到到最後一個元素,則表示陣列有序。
圖示
可以通過動畫演示理解, 以下網上找的動畫。如果你想操作不同的引數來演示,可以上這個網站visualgo.net動手試試。
程式碼實現
關於程式碼,README中程式碼只有實現演算法的函式,具體執行的程式碼,請檢視該目錄下的檔案。
程式碼如下:
const selectSort = (array) => {
// 不修改原陣列
const originValues = array.slice();
const length = originValues.length;
// 迭代次數 陣列長度-1, 因為前n個元素有序,則全部有序
for (let i = 0; i < length - 1; i++) {
// 把當前無序子列的第一個元素當做最小值
let minIndex = i;
// 找出最小值的索引
for (let j = i+1; j < length; j++) {
if (originValues[j] < originValues[minIndex]) {
minIndex = j;
}
}
// 如果最小值不為當前值,交換
if (minIndex !== i) {
const tmp = originValues[i];
originValues[i] = originValues[minIndex];
originValues[minIndex] = tmp;
}
}
return originValues;
};
複製程式碼
選擇排序還是比較簡單的,基本知道原理,看了註釋就很明白了。有一點要說的是,就是在找最小值這個步驟。很多文章的實現,在發現當前值小於當前最小值時,就交換元素。這種交換還是沒必要的,只要先記住最小值得下標minIndex
就可以,最後一步來做交換。這樣就減少了很多不必要的交換要素,後來發現和wikipedia的實現一模一樣(第一次也是唯一一次,哈哈)。
演算法分析
時間複雜度
選擇排序,不管陣列正序還是逆序,都是一樣的操作。最優複雜度和最差複雜度都是O(n2)。
穩定性
因為可能存在和最小值元素交換是,把數值相同的元素順序調換,所以,選擇排序是不穩定的排序。
舉個例子吧:
[3] 5 2 (3) 1
複製程式碼
由於最小的元素1在最後一個,需要和[3]
元素交換,此時[3]
就到(3)
後面了。
有文章說選擇排序是穩定的,其實看具體的實現。在《演算法》第四版217頁上作者已經說了,有很多辦法可以將任意排序演算法變成穩定的,但是,往往需要額外的時間或者空間。摘自知乎
總結
本章節介紹了幾種選擇排序的實現。選擇排序應該是最簡單的排序了,不過效率也是很低,複雜度還是O(n2)。
參考
[1] 動畫演示
[2] tutorials point 教程
[3] 選擇排序究竟屬於穩定排序還是不穩定排序?
[4] Sorting Algorithms
[5] 凱耐基梅隆大學資料結構與演算法-排序演算法
[6] CMU algorithm complexity