Go語言插入排序(包括shell希爾排序)
阿新 • • 發佈:2018-12-31
package main
import (
"fmt"
)
/*
插入排序不適合對於資料量比較大的排序應用。但是,如果需要排序的資料量很小,比如量級小於千,那麼插入排序還是一個不錯的選擇。 插入排序在工業級庫中也有著廣泛的應用,在STL的sort演算法和stdlib的qsort演算法中,都將插入排序作為快速排序的補充,用於少量元素的排序(通常為8個或以下)
*/
func insert_sort(a []int){
for i:=1;i< len(a);i++{// 類似抓撲克牌排序
j := i-1 // 拿在左手上的牌總是排序好的,i-1對應的資料是已排序的最大值
get := a[i] // 右手抓到一張撲克牌
for j >= 0 && a[j] > get{// 將抓到的牌與左手牌從右向左進行比較
a[j+1] = a[j] //右移大的牌
j--
}
a[j+1] = get// 直到該手牌比抓到的牌小(或二者相等),將抓到的牌插入到該手牌右邊
//(相等元素的相對次序未變,所以插入排序是穩定的)
}
}
/*
當n較大時,二分插入排序的比較次數比直接插入排序的最差情況好得多,但比直接插入排序的最好情況要差,所當以元素初始序列已經接近升序時,直接插入排序比二分插入排序比較次數少。二分插入排序元素移動次數與直接插入排序相同,依賴於元素初始序列。
*/
//採用二分查詢法來減少比較操作的次數,我們稱為二分插入排序
func half_insert_sort(a []int){
for i:=1;i < len(a);i++{
get := a[i]
left := 0
right := i - 1
for left <= right {// 採用二分法定位新牌的位置
mid := (left + right)/2
if a[mid] > get {
right = mid - 1
}else {
left = mid + 1//left的位置就是新牌要插入的位置
}
}
for j := i-1;j>=left;j--{
a[j+1] = a[j]
}
a[left] = get// 將抓到的牌插入手牌
}
}
func shell_sort(a []int){
//step := len(a)/3 + 1
step := 0 // 生成初始增量,這裡尋找初始增量很重要
for step <= len(a){
step = 3*step +1
}
for step > 0{
for i:=step;i< len(a);i++{// 類似抓撲克牌排序
j := i-step // 拿在左手上的牌總是排序好的,i-1對應的資料是已排序的最大值
get := a[i] // 右手抓到一張撲克牌
for j >= 0 && a[j] > get{// 將抓到的牌與左手牌從右向左進行比較
a[j+step] = a[j] //右移大的牌
j = j - step
}
a[j+step] = get// 直到該手牌比抓到的牌小(或二者相等),將抓到的牌插入到該手牌右邊
//(相等元素的相對次序未變,所以插入排序是穩定的)
}
fmt.Println(step)
step = (step-1)/3 // 遞減增量
}
}
func main(){
c := [...]int{8,7,6,1,4,3,2,5}
insert_sort(c[:])
fmt.Println(c)
b := [...]int{8,7,6,1,4,3,2,5}
half_insert_sort(b[:])
fmt.Println(b)
d := [...]int{8,7,6,1,4,3,2,5}
shell_sort(d[:])
fmt.Println(d)
}
希爾排序通過將比較的全部元素分為幾個區域來提升插入排序的效能。這樣可以讓一個元素可以一次性地朝最終位置前進一大步。然後演算法再取越來越小的步長進行排序,演算法的最後一步就是普通的插入排序,但是到了這步,需排序的資料幾乎是已排好的了(此時插入排序較快)。
假設有一個很小的資料在一個已按升序排好序的陣列的末端。如果用複雜度為O(n^2)的排序(氣泡排序或直接插入排序),可能會進行n次的比較和交換才能將該資料移至正確位置。而希爾排序會用較大的步長移動資料,所以小資料只需進行少數比較和交換即可到正確位置。
希爾排序是不穩定的排序演算法,雖然一次插入排序是穩定的,不會改變相同元素的相對順序,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最後其穩定性就會被打亂。
比如序列:{ 3, 5, 10, 8, 7, 2, 8, 1, 20, 6 },h=2時分成兩個子序列 { 3, 10, 7, 8, 20 } 和 { 5, 8, 2, 1, 6 } ,未排序之前第二個子序列中的8在前面,現在對兩個子序列進行插入排序,得到 { 3, 7, 8, 10, 20 } 和 { 1, 2, 5, 6, 8 } ,即 { 3, 1, 7, 2, 8, 5, 10, 6, 20, 8 } ,兩個8的相對次序發生了改變。