折半插入排序
直接插入排序在确定插入位置时,需要逐个比较已排序区间的元素,当已排序区间较长时,比较次数较多。折半插入排序则利用"已排序区间是有序的"这一特性,通过折半查找快速确定插入位置,从而减少比较次数,是对直接插入排序的高效优化。
1. 折半插入排序的执行流程
我们以数组arr = {49, 38, 65, 97, 76, 13, 27, 49}为例,详细演示折半插入排序的每一步:
- 初始状态 :已排序区间为
[49],未排序区间为{38, 65, 97, 76, 13, 27, 49}。 - 第1次插入(处理38) :
目标是将38插入到已排序区间[49]的合适位置。
① 折半查找插入位置:定义low=0,high=0(已排序区间长度为1),计算mid = (low+high)/2 = 0。比较arr[mid]=49与38,38 < 49,所以插入位置在mid左侧,更新high=mid-1=-1,此时low > high,确定插入位置为low=0。
② 移动元素并插入:将49后移一位,把38插入到arr[0]位置。数组变为{38, 49, 65, 97, 76, 13, 27, 49},已排序区间扩展为[38, 49]。 - 第2次插入(处理65) :
已排序区间为[38, 49],处理未排序区间的65。
① 折半查找:low=0,high=1,mid=(0+1)/2=0。比较arr[mid]=38与65,65 > 38,更新low=mid+1=1;再次计算mid=(1+1)/2=1,比较arr[mid]=49与65,65 > 49,更新low=mid+1=2,此时low > high,确定插入位置为low=2。
② 移动元素并插入:65直接插入到49后面,数组不变,已排序区间扩展为[38, 49, 65]。 - 第3次插入(处理97) :
已排序区间为[38, 49, 65],处理97。
折半查找后,发现97大于已排序区间的最大元素65,直接插入到末尾,数组不变。 - 第4次插入(处理76) :
已排序区间为[38, 49, 65, 97],处理76。
① 折半查找:low=0,high=3,mid=1(arr[1]=49 < 76),更新low=2;mid=2(arr[2]=65 < 76),更新low=3;mid=3(arr[3]=97 > 76),更新high=2,此时low > high,确定插入位置为low=3。
② 移动元素并插入:将97后移,把76插入到65和97之间,数组变为{38, 49, 65, 76, 97, 13, 27, 49}。 - 后续插入(处理13、27、49) :
按照同样的折半查找逻辑,13会被插入到最前面,27插入到13之后,最后一个49会插入到已排序区间中第一个49的后面(保持相同元素的相对顺序,算法稳定)。最终数组变为{13, 27, 38, 49, 49, 65, 76, 97}。
2. 折半插入排序的代码实现
以下是折半插入排序的C语言实现,核心是用折半查找确定插入位置,再移动元素完成插入:
c
void BinaryInsertSort(int arr[], int n) {
int i, j, low, high, mid, temp;
for (i = 1; i < n; i++) {
temp = arr[i]; // 取出未排序区间的第一个元素
low = 0; high = i - 1; // 已排序区间的范围[low, high]
while (low <= high) { // 折半查找插入位置
mid = (low + high) / 2;
if (arr[mid] > temp) high = mid - 1;
else low = mid + 1;
}
for (j = i; j > low; j--) { // 移动元素,腾出插入位置
arr[j] = arr[j - 1];
}
arr[low] = temp; // 插入元素
}
}
代码说明:
- 外层循环
i遍历未排序区间的起始位置; - 折半查找阶段:通过
low和high缩小范围,最终low即为插入位置; - 元素移动阶段:从
i位置开始,将元素后移,直到low位置,再将temp插入。
3. 折半插入排序的性能与特性
- 时间复杂度 :
折半查找的时间复杂度为 O ( log n ) O(\log n) O(logn),但元素移动的次数仍与直接插入排序相同,因此整体时间复杂度仍为 O ( n 2 ) O(n^2) O(n2)。不过,由于减少了比较次数,实际运行效率比直接插入排序更高。 - 空间复杂度 :仅需一个临时变量
temp,空间复杂度为 O ( 1 ) O(1) O(1)。 - 稳定性 :相同元素插入时,相对顺序不会改变,因此折半插入排序是稳定的。
4. 适用场景
折半插入排序适合已排序区间较长 的场景,此时折半查找的优势能充分体现,减少大量比较操作。例如,对基本有序的数组进行排序,或数据量中等(如 n n n在几百到几千之间)的情况,它的效率比直接插入排序更优。
综上,折半插入排序通过折半查找优化了"确定插入位置"的过程,在比较次数上有明显优势,且保持了直接插入排序的稳定性和空间效率,是插入排序家族中更高效的实现方式。理解其"折半查找+元素移动"的逻辑,能为后续学习更复杂的排序算法提供思路。