151
社区成员
发帖
与我相关
我的任务
分享折半插入排序是直接插入排序的优化版本。直接插入排序在向前找插入位置时,采用顺序查找,比较次数多、效率低;由于前面的有序区始终有序,因此可以用 折半查找(二分查找) 快速定位插入位置。
核心:
划分有序区、无序区,和直接插入排序一致;
利用二分,快速算出待插入元素应该放的下标;
统一批量后移元素,再放入目标值;
只减少比较次数,元素移动次数不变。
初始:数组第 0 号元素为有序区,从 i=1开始遍历无序区;取出当前待插入元素 temp = a[i];在有序区间 [0, i−1] 内,用折半查找找到插入位置 low;将插入位置到 i−1 的所有元素整体向后移动一位;将 temp 放入空位,完成一趟插入;重复直到全部有序。
#include <stdio.h>
// 折半插入排序
void BinaryInsertSort(int a[], int n)
{
int i, low, high, mid;
int temp;
// 无序区从第2个元素开始
for (i = 1; i < n; i++)
{
temp = a[i]; // 暂存待插入元素
low = 0;
high = i - 1; // 划定有序区范围
// 1.折半查找插入位置
while (low <= high)
{
mid = (low + high) / 2;
if (temp < a[mid])
high = mid - 1;
else
low = mid + 1;
}
// 循环结束:low 即为插入位置
// 2.元素统一后移,腾出位置
for (int j = i - 1; j >= low; j--)
{
a[j + 1] = a[j];
}
// 3.插入元素
a[low] = temp;
}
}
// 打印数组
void Print(int a[], int n)
{
for (int i = 0; i < n; i++)
printf("%d ", a[i]);
printf("\n");
}
int main()
{
int arr[] = {49, 38, 65, 97, 76, 13, 27};
int len = sizeof(arr) / sizeof(arr[0]);
BinaryInsertSort(arr, len);
Print(arr, len);
return 0;
}
for(i = 1; i < n; i++)i 指向无序区第一个元素,有序区为 [0,i−1]。
temp = a[i]保存待插入数值,防止后移覆盖丢失。
low = 0 , high = i - 1限定二分查找范围:整个前面的有序区间。
二分核心循环
temp < a[mid]:目标值更小,去左半区间,high = mid - 1
temp >= a[mid]:去右半区间,low = mid + 1
循环结束时,low 就是唯一正确插入下标。
for(j = i-1; j >= low; j--)从后往前,把插入位置后面所有元素集体后移一位。
a[low] = temp空位填入待插入元素,本趟排序完成。
时间复杂度
比较次数:由 O(n 2 ) 优化为 O(nlog 2 n)
元素移动次数:仍为 O(n 2 )
整体平均、最坏时间复杂度:O(n 2 )
空间复杂度仅常数辅助变量:O(1),就地排序
稳定性稳定排序相等元素不会交换相对位置。
相同点
都属于插入排序;
稳定、原地排序;
最坏 / 平均时间复杂度 O(n 2 )。
不同点
直接插入:顺序找位置,比较多、效率低;
折半插入:二分找位置,大幅减少比较次数,适合数据量稍大的有序类序列。