“排序是計算機的核心內容。事實上,從很多方面看,如果沒有排序,計算機就不會變成現實。”《算法之美:指導工作與生活的算法》
算法
|
最壞運行時間
|
平均運行時間
|
是否穩定
|
原址排序
|
插入排序
|
Θ(n^2)
|
Θ(n^2)
|
是
|
是
|
歸併排序
|
Θ(nlgn)
|
Θ(nlgn)
|
是
|
否
|
堆排序
|
O(nlgn)
|
----
|
否
|
是
|
快速排序
|
Θ(n^2)
|
Θ(nlgn)
|
否
|
是
|
計數排序
|
Θ(n+k)
|
Θ(n+k)
|
是
|
否
|
基數排序
|
Θ(d(n+k))
|
Θ(d(n+k))
|
是
|
否
|
桶排序
|
Θ(n^2)
|
Θ(n)
|
是
|
否
|
|
|
|
|
|
1. 插入排序
-
黃色代表尚未實現排序的元素
-
綠色代表已經排序好的元素
-
每一輪循環確定一個元素的位置
-
箭頭代表元素之間的比較,如果比較發現左邊的元素大於右邊的該元素,則兩個位置進行交換,如果小於則退出本輪循環
func insertSort(arr []int){
size := len(arr)
if size == 0 || size == 1{
return
}
for j:=1;j<=size-1;j++{
current := j
for i:=j-1;i>=0;i--{
if arr[current] < arr[i] {
arr[current], arr[i] = arr[i],arr[current]
current = i
}else{
break
}
}
}
}
2. 歸併排序
-
分解: 將待排序的n個元素序列切分爲兩個n/2的子序列
-
解決: 使用同樣的算法歸併排序遞歸排序兩個子序列
-
合併: 合併兩個已經排序的子序列用以差生已排序的答案
func mergeSort(arr []int) []int{
size := len(arr)
if size < 2 {
return arr
}
left := mergeSort(arr[0:size/2])
right := mergeSort(arr[size/2:])
leftSize := len(left)
rightSize := len(right)
i := 0
j := 0
var result []int
for i < leftSize && j <rightSize{
if left[i] < right[j]{
result = append(result, left[i])
i++
}else{
result = append(result, right[j])
j++
}
}
if i < leftSize {
result = append(result, left[i:]...)
}else if j < rightSize{
result = append(result, right[j:]...)
}
return result
}
3. 堆排序
-
構建堆結構(Build), 從一個普通數組轉換爲具有堆性質的數組
-
維護堆結構(Heapify),只是針對其中一個節點,保持節點以及子樹爲一個堆的過程,用於維護堆的性質(大頂或小頂),時間複雜度爲O(logn)
-
堆排序(HeapSort): 對一個數組實現其原址排序。
-
我們選擇處理的元素爲1
-
由於元素1的兩個子元素爲8和3,均大於1,我們選擇最大8與當前的元素1進行交換
-
交換完成後元素1下移到8的位置。
-
比較元素1和新的兩個子元素,繼續重複相同的流程,直到當前的節點滿足大頂的要求
func heapify(arr []int,size int , index int){
left := 2 * index
right := 2 * index +1
max := index-1
if size >= left && arr[max] < arr [left-1]{
max = left-1
}
if size >= right && arr[max] < arr[right-1]{
max = right-1
}
if max != index-1 {
arr[max], arr[index-1] = arr[index-1], arr[max]
heapify(arr, size, max+1)
}
}
func heapSort(arr []int){
size := len(arr)
for i:= size/2; i>=1; i--{
heapify(arr,size, i)
}
lenOfArr := size
for i:=size-1; i >=1 ;i--{
arr[i], arr[0] = arr[0], arr[i]
heapify(arr,lenOfArr-1, 1)
lenOfArr--
}
}
4. 快速排序算法
-
分解: 數組劃分爲兩個子數組,其中左數組中所有元素都小於等於Arr[x], 右數組中所有元素都大於等於Arr[x], x爲開始選擇的其中一個元素索引。
-
解決: 通過遞歸調用快速排序,對於其左數組和右數組進行不斷的切分和排序
-
合併: 子數組爲原址排序,因此本身就已經是排序好的,無需類似於歸併排序一樣的合併操作。
func quickSort(a []int) []int{
size := len(a)
if size < 2 {
return a
}
left, right := 0, size-1
for i:=0; i< size-1; i++{
if a[i] < a[right]{
a[left], a[i] = a[i], a[left]
left++
}
}
a[left], a[right] = a[right], a[left]
quickSort(a[:left])
quickSort(a[left+1:])
return a
}
func quickSort(a []int) []int{
size := len(a)
if size < 2 {
return a
}
left, right := 0, size-1
pivot := rand.Int() % size
a[pivot], a[right] = a[right], a[pivot]
for i:=0; i< size-1; i++{
if a[i] < a[right]{
a[left], a[i] = a[i], a[left]
left++
}
}
a[left], a[right] = a[right], a[left]
quickSort(a[:left])
quickSort(a[left+1:])
return a
}
5. 計數排序算法
-
初始化一個[0..k]的計數數組,用於存儲n個數中每個不同的數出現的次數
-
循環迭代n數組中的元素,並統計每個數的個數進行累加
-
最後循環計數數組產生輸出結果
func countSort(arr []int, max int) []int{
size := len(arr)
if size < 2 {
return arr
}
countArr := make([]int, max+1)
result := make([]int, len(arr))
for _, v := range arr{
countArr[v]++
}
for i:=1; i< max+1 ;i ++{
countArr[i] = countArr[i-1]+countArr[i]
}
for i:=size-1;i>=0;i--{
result[countArr[arr[i]]-1] =arr[i]
countArr[arr[i]]--
}
return result
}
6. 基數排序算法
- 第一次排序的時候僅僅排序個位數上的數組,形成排序結果。
- 第二次排序在上一次排序基礎上再對於十位數上數字進行排序,
- 最後一次排序對於百位數進行排序。我們排序完成後可以看到整個的排序已經完成了。
func radixSort(arr []int)[]int {
largestNum := findLargestNum(arr)
size := len(arr)
significantDigit := 1
semiSorted := make([]int, size, size)
for largestNum / significantDigit > 0 {
bucket := [10]int{0}
for i := 0; i < size; i++ {
bucket[(arr[i] / significantDigit) % 10]++
}
for i := 1; i < 10; i++ {
bucket[i] += bucket[i - 1]
}
for i := size - 1; i >= 0; i-- {
bucket[(arr[i] / significantDigit) % 10]--
semiSorted[bucket[(arr[i] / significantDigit) % 10]] = arr[i]
}
for i := 0; i < size; i++ {
arr[i] = semiSorted[i]
}
significantDigit *= 10
}
return arr
}
7. 桶排序算法
func bucketSort(arr []float64){
buckets := make([][]float64,10)
for _, v := range arr{
bucketIndex := int(v * 10) % 10
buckets[bucketIndex]= append(buckets[bucketIndex], v)
}
for _ , bucket := range buckets{
sort.Float64s(bucket)
}
currentIndex := 0
for _, bucket := range buckets{
for _, v := range bucket{
arr[currentIndex] = v
currentIndex++
}
}
}