折半插入排序(Binary Insertion Sort)是对直接插入排序算法的改进,可以减少比较和移动次数这两方面着手
先了解
折半查找,又称为二分查找(Binary Search),是一种效率较高的查找方法。折半查找要求查找表有序,即表中元素按关键字有序,而且必须是顺序存储
思想
首先,将给定的关键字 k 与有序表的中间位置上的元素进行比较
,若相等,则查找成功;否则,中间元素将有序表分成两个部分,前一部分的元素均小于中间元素,而后一部分的元素均大于中间元素。因此,k 与中间元素比较后,若 k 小于中间元素,则应该在前一部分中查找,否则在后一部分查找。重复以上过程,直至查找成功或查找失败
正文
在前半部分已排好序的序列的基础下,不用按顺序依次寻找插入点,可以采用折半查找的方法来加快寻找插入点的速度,折半插入排序算法是一种稳定的排序算法
具体操作
在将一个新元素插入已排好序的数组的过程中,寻找插入点时,将待插入区域(已排好的序列)的首元素位置设置为 low(低位点) ,末元素设置为high(高位点)。将待插入元素与数组[mid](其中mid = low + (high-low) / 2
)相比较,如果比参考元素小,则选择数组下标从low到mid - 1作为新的插入区域(即修改高位点high = mid - 1
),否则选择 mid + 1 到 high 作为新的插入区域(即修改低位点low = mid + 1
),循环直至low <= high不成立,即将 low 位置及之后所有元素后移一位,并将新元素插入数组[low]
的位置
测试用例: 使用折半插入排序算法将数组 { 36,80,45,66,22,9,16,36 } 进行升序排序
具体一步的实现过程
代码实现
/**
* 折半插入排序
* 首先要要在一组有序列中,进行折半查找
* 折半插入排序比直接插入排序明显减少了关键字间的比较次数,但元素移动次数不变
* 时间复杂度为O(n^2)
* @param nums 数组
* @param size 数组大小
* @return 返回排列好的数组
*/
public int[] binaryInsertionSort(int[] nums, int size){
int temp;
int position, j, low, mid, high;
// 拆分区域,分为已排序好的区域以及未排序好的区域
for (position = 1; position < size; position++) {
temp = nums[position];
low = 0; high = position - 1; // low代表折半后有序列的低位点,high代表折半后有序列的高位点
// 循环寻找合适的下标位置,实质上就是给变量low取最终的值
while (low <= high){
mid = low + (high - low) / 2; // 不用mid = (high + low) / 2的原因是,防止整数溢出问题
if (temp < nums[mid]) // 由高低点的中间位置的元素,比较temp的值,如果在低半区
high = mid - 1; // 重新给高位点赋值
else low = mid + 1; // 如果在高半区,就重新给低位点赋值
}
// 由以上的循环找出low低位之后,通过循环从temp位置的前一个开始,往后移
// low低位的位置就是temp最终的位置,所以递减循环到low低位的位置时结束
for (j = position -1; j >= low; j--)
nums[j+1] = nums[j];
nums[low] = temp;
}
return nums;
}
时间复杂度
折半插入排序算法比直接插入排序算法明显减少了关键字之间比较的次数,因此速度比直接插入排序算法快,但元素移动的次数没有变,所以折半插入排序算法的时间复杂度仍然为
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度
在折半插入排序的过程中,只需要一个元素大小的辅助空间用于存放待插入的元素,因此空间复杂度为
O
(
1
)
O(1)
O(1)