選擇排序,通常咱們指的是簡單選擇排序,也能夠叫直接選擇排序,它不像冒泡排序同樣相鄰地交換元素,而是經過選擇最小的元素,每輪迭代只需交換一次。雖然交換次數比冒泡少不少,但效率和冒泡排序同樣的糟糕。算法
選擇排序屬於選擇類排序算法。segmentfault
我打撲克牌的時候,會習慣性地從左到右掃描,而後將最小的牌放在最左邊,而後從第二張牌開始繼續從左到右掃描第二小的牌,放在最小的牌右邊,以此反覆。選擇排序和我玩撲克時的排序特別類似。數組
如今有一堆亂序的數,好比:5 9 1 6 8 14 6 49 25 4 6 3
。數據結構
第一輪迭代,從第一個數開始,左邊到右邊進行掃描,找到最小的數 1,與數列裏的第一個數交換位置。併發
第二輪迭代,從第二個數開始,左邊到右邊進行掃描,找到第二小的數 3,與數列裏的第二個數交換位置。數據結構和算法
第三輪迭代,從第三個數開始,左邊到右邊進行掃描,找到第三小的數 4,與數列裏的第三個數交換位置。函數
第N輪迭代:....優化
通過交換,最後的結果爲:1 3 4 5 6 6 6 8 9 14 25 49
,咱們能夠看到已經排好序了。code
每次掃描數列找出最小的數,而後與第一個數交換,而後排除第一個數,從第二個數開始重複這個操做,這種排序叫作簡單選擇排序。協程
舉個簡單例子,選擇排序一個 4 個元素的數列:4 2 9 1
:
[]表示排好序 起始: 4 2 9 1 未排序數列從左掃描最小的數是 1,與第一個元素 4 交換,交換 1,4 一輪: [1] 2 9 4 未排序數列從左掃描最小的數是 2,不須要交換 二輪: [1 2] 9 4 未排序數列從左掃描最小的數是 4,與第三個元素 9 交換,交換 4,9 三輪: [1 2 4] 9 未排序數列只有 1 個數,結束 結果: [1 2 4 9]
比較的次數和冒泡排序同樣多,由於掃描過程也是比較的過程,只不過交換的次數減小爲每輪 1 次。最佳和最壞時間複雜度仍然是:O(n^2)
。
選擇排序是一個不穩定的排序算法,好比數組:[5 6 5 1]
,第一輪迭代時最小的數是1
,那麼與第一個元素5
交換位置,這樣數字1
就和數字5
交換了位置,致使兩個相同的數字5
排序後位置變了。
package main import "fmt" func SelectSort(list []int) { n := len(list) // 進行 N-1 輪迭代 for i := 0; i < n-1; i++ { // 每次從第 i 位開始,找到最小的元素 min := list[i] // 最小數 minIndex := i // 最小數的下標 for j := i + 1; j < n; j++ { if list[j] < min { // 若是找到的數比上次的還小,那麼最小的數變爲它 min = list[j] minIndex = j } } // 這一輪找到的最小數的下標不等於最開始的下標,交換元素 if i != minIndex { list[i], list[minIndex] = list[minIndex], list[i] } } } func main() { list := []int{5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6, 3} SelectSort(list) fmt.Println(list) }
每進行一輪迭代,咱們都會維持這一輪最小數:min
和最小數的下標:minIndex
,而後開始掃描,若是掃描的數比該數小,那麼替換掉最小數和最小數下標,掃描完後判斷是否應交換,而後交換:list[i], list[minIndex] = list[minIndex], list[i]
。
上面的算法須要從某個數開始,一直掃描到尾部,咱們能夠優化算法,使得複雜度減小一半。
咱們每一輪,除了找最小數以外,還找最大數,而後分別和前面和後面的元素交換,這樣循環次數減小一半,如:
package main import "fmt" func SelectGoodSort(list []int) { n := len(list) // 只需循環一半 for i := 0; i < n/2; i++ { minIndex := i // 最小值下標 maxIndex := i // 最大值下標 // 在這一輪迭代中要找到最大值和最小值的下標 for j := i + 1; j < n-i; j++ { // 找到最大值下標 if list[j] > list[maxIndex] { maxIndex = j // 這一輪這個是大的,直接 continue continue } // 找到最小值下標 if list[j] < list[minIndex] { minIndex = j } } if maxIndex == i && minIndex != n-i-1 { // 若是最大值是開頭的元素,而最小值不是最尾的元素 // 先將最大值和最尾的元素交換 list[n-i-1], list[maxIndex] = list[maxIndex], list[n-i-1] // 而後最小的元素放在最開頭 list[i], list[minIndex] = list[minIndex], list[i] } else if maxIndex == i && minIndex == n-i-1 { // 若是最大值在開頭,最小值在結尾,直接交換 list[minIndex], list[maxIndex] = list[maxIndex], list[minIndex] } else { // 不然先將最小值放在開頭,再將最大值放在結尾 list[i], list[minIndex] = list[minIndex], list[i] list[n-i-1], list[maxIndex] = list[maxIndex], list[n-i-1] } } } func main() { list := []int{5} SelectGoodSort(list) fmt.Println(list) list1 := []int{5, 9} SelectGoodSort(list1) fmt.Println(list1) list2 := []int{5, 9, 1} SelectGoodSort(list2) fmt.Println(list2) list3 := []int{5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6, 3} SelectGoodSort(list3) fmt.Println(list3) list4 := []int{5, 9, 1, 6, 8, 14, 6, 49, 25, 4, 6} SelectGoodSort(list4) fmt.Println(list4) }
輸出:
[5] [5 9] [1 5 9] [1 3 4 5 6 6 6 8 9 14 25 49] [1 4 5 6 6 6 8 9 14 25 49]
優化後的選擇排序仍是很慢,它很好理解,可是仍是不建議在工程上使用。
我是陳星星,歡迎閱讀我親自寫的 數據結構和算法(Golang實現),文章首發於 閱讀更友好的GitBook。