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的相对次序发生了改变。