算法简介

折半插入排序和直接插入排序很相似,都是先假定前面一段序列是有序,然后从整个序列无序的第一个元素起开始遍历,不断的往这个有序的序列中插入,直接插入是一个紧挨着一个找,找到过程中元素不断后移,而这般插入排序利用这半查找的方式来快速找到这个待插入元素再有序序列中那个位置,找到这个位置之后,把相应位置的元素不断后移即可

时间复杂度 O(n^2) 和直接插入排序一样,空间复杂度 O(1),是稳定的排序算法

Java 实现

思路

有一点不容易思清楚,折半假如以中间点和左边都作为整个左边,以中间点+1往右的区域作为整个右边,是没问题的,比如下标有 0 1 2,这样来看 ,0 1 作为左边,2 作为右边,然后左边 left = 0, right = 1 右边 left = 2, right = 2,之后假如left = 0, right = 1继续折半为左边 left = 0, right = 0 右边 left = 1, right = 1,我们发现最终的结果都是left = right,所以我们循环中介条件就是left!=right,但是后面要比较下这个下标的值是大于要插入的元素还是小于要插入的元素。那如果我就是想把包括中间值和中间值右边的区域作为整个右边行吗,这样会很麻烦,比方说现在右边下标只有 4 和 5,我们折半一下左边 left = 4, right = 3 右边 left = 4, right = 5,那么这个 4 和 5 就会不断循环下去

代码实现

// 折半插入排序
public int[] bineryInsertSort(int[] arr) {
    for (int i = 1; i <= arr.length; i++) {
        int left = 0;
        int right = i - 1;
        while (right != left) {
            // 如果在右半边
            if (arr[(left+right)/2] < arr[i])
                left = (left+right)/2 + 1;
            // 如果在左半边
            if (arr[(left+right)/2] >= arr[i])
                right = (left+right)/2;
        }
        int temp = arr[i];
        // 如果该元素比折半找到的值大
        if (arr[i] >= arr[right]) {
            for (int j = i - 1; j > right; j--) {
                a[j+1] = a[j];
            }
            a[j+1] = temp;
        }
        // 如果该元素比折半找到的值小
        if (arr[i] < arr[left]) {
            for (int j = i - 1; j >= right; j--) {
                a[j+1] = a[j];
            }
            a[j+1] = temp;
        }
    }
    return arr;
}
时间复杂度

时间复杂度 O(n^2),和直接插入排序一样

空间复杂度

空间复杂度 O(1),和直接插入排序一样

算法稳定性

一般来说这般插入排序是稳定的排序算法