折半插入排序(binary insertion sort)是对插入排序算法的一种改进,由于排序算法过程中,就是不断的依次将元素插入前面已排好序的序列中。由于前半部分为已排好序的数列,这样我们不用按顺序依次寻找插入点,可以采用折半查找的方法来加快寻找插入点的速度。
算法实现
/**
* 折半插入排序(升序)
* @param {Array} seq - 待排序列
* @author 范围兄 <ambit_tsai@qq.com>
*/
function binaryInsertionSort(seq){
let i, j, len, tmp, low, mid, high;
for(i=1, len=seq.length; i<len; ++i){
tmp = seq[i]; // 备份本次记录
low = 0;
high = i - 1;
while(low <= high){ // 在[low..high]区间中查找插入位置
mid = (low+high)/2 >> 0; // 折半
if(seq[mid] > seq[i]){
high = mid - 1; // 插入点在低半区
}else{
low = mid + 1; // 插入点在高半区
}
}
for(j=i-1; j>=low; --j){
seq[j+1] = seq[j]; // 记录后移
}
seq[low] = tmp; // 插入本次记录
}
}
时间复杂度
折半插入排序是一种稳定的排序算法,比直接插入排序明显减少了关键字之间比较的次数,因此速度比直接插入排序算法快,但记录移动的次数没有变,所以折半插入排序算法的时间复杂度仍然为O(n2),与直接插入排序算法相同。
然而,当待排序列已是最佳次序时(在本文中“升序”为最佳次序),直接插入排序算法进行 n-1 趟排序后,记录无需移动。由此,直接插入排序最好的时间复杂度为O(n)。
排序方法 | 时间复杂度 | ||
---|---|---|---|
最好 | 最坏 | 平均 | |
折半插入排序 | O(n2) | O(n2) | O(n2) |
直接插入排序 | O(n) | O(n2) | O(n2) |
代码优化
当待排序列已是最佳次序时,只要将本次记录与有序序列的最后一个记录(即本次记录的前一个记录)比较,便可以结束本轮排序,无需进行后续运算。
/**
* 优化的折半插入排序(升序)
* @param {Array} seq - 待排序列
* @author 范围兄 <ambit_tsai@qq.com>
*/
function binaryInsertionSort(seq){
let i, j, len, tmp, low, mid, high;
for(i=1, len=seq.length; i<len; ++i){
if(seq[i] >= seq[i-1]) continue; // 与前一个记录比较
tmp = seq[i];
low = 0;
high = i - 2; // 下标high前移2位
while(low <= high){
mid = (low+high)/2 >> 0;
if(seq[mid] > seq[i]){
high = mid - 1;
}else{
low = mid + 1;
}
}
for(j=i-1; j>=low; --j){
seq[j+1] = seq[j];
}
seq[low] = tmp;
}
}
时间复杂度
排序方法 | 时间复杂度 | ||
---|---|---|---|
最好 | 最坏 | 平均 | |
折半插入排序(优化) | O(n) | O(n2) | O(n2) |
直接插入排序 | O(n) | O(n2) | O(n2) |