二分插入排序

437 阅读1分钟

利用二分查找优化后的插入排序

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--
    }
}