利用二分查找优化后的插入排序
const tinySize = 17
// 入口
func BinaryInsertionSort(arr []int) {
l := len(arr)
if l < 2 {
return
}
for i := makeAscending(arr); i < l; i++ {
n := arr[i]
j := i - 1
if i < tinySize { // 小区间,直接遍历
for j >= 0 && arr[j] > n {
arr[j+1] = arr[j]
j--
}
arr[j+1] = n
} else { // 使用二分查找
k := binarySearch(arr, 0, j, n)
if k < i {
c := i - k
// 需要移动元素时,
// 少量元素,直接拷贝;
// 默认使用buildin函数:copy
switch c {
case 2:
arr[k+2] = arr[k+1]
fallthrough
case 1:
arr[k+1] = arr[k]
default:
copy(arr[k+1:], arr[k:i])
}
arr[k] = n
}
}
}
}
// 在arr的[low,high]区间中找n的最右的按顺序插入的位置
// 返回值:取值范围[low,high+1]
func binarySearch(arr []int, low, high, n int) int {
for low <= high {
mid := low + (high-low)>>1
v := arr[mid]
if v <= n {
low = mid + 1
} else {
high = mid - 1
}
}
return low
}
// 先检查有没有以0索引开头的已序区间。如果有,就可以简化开头部分的排序了。
// 返回值:非降序区间的结束位置的下一个位置
func makeAscending(arr []int) int {
l := len(arr)
if arr[0] <= arr[1] { // 非递减,arr[i] <= arr[i+1] <= arr[i+2] ...
i := 2
for i < l {
if arr[i-1] <= arr[i] {
i++
} else {
break
}
}
return i
} else { // 递减
// 要求严格降序,因为后面会做区间翻转,为保证插入排序的稳定性,不能交换等值元素。
// arr[i] > arr[i+1] > arr[i+2] ...
i := 2
for i < l {
if arr[i-1] > arr[i] {
i++
} else {
break
}
}
reverseArray(arr, 0, i-1)
return i
}
}
// 翻转arr的区间[low,high]
func reverseArray(arr []int, low, high int) {
for low < high {
arr[low], arr[high] = arr[high], arr[low]
low++
high--
}
}