1.什么是折半插入排序
(Binary Insertion Sort)折半插入排序又称二分插入排序,是插入排序的一种。
折半插入排序是对直接插入排序的一种改进。
改进?
(直接插入排序)线性查找 —> 折半查找
减少比较次数
基本思路:
每次插入操作,采用折半查找的方式,查找插入位置,
然后再插入元素(先挪后插入)。
2.算法思路
step 1:找插入位置(Insertion Position)
待查找范围的下标 [low,high]
每次跟中间元素PK mid=(low+hiqh)/ 2
根据PK结果,调整待查找范围(改变low or high)
如此重复,直到查找不成功(1ow>high了)或 查找到?
(1)查找不成功
(原有序表中没有待插入的元素)时,插入位置的确定
Insertion Position = high + 1 (low)
(2)查找成功
(原有序表中有待插入的元素)时,插入位置的确定
靠后的插入位置,更优化(后续挪动次数会少一些)
low = mid+1,when x == a[mid]
…
=> 查找不成功
step 2:插入操作
(1)先挪元素
[last,—> Insertion Position]
high+1
(2)插入操作
x->[Insertion Position]
x->a[high +1]
代码实现
#include <stdio.h>
#define N 5
/*
BinInsert:一次折半插入
@a:有序表,数组名
@n:有序表的元素个数
a[0] a[1] ... a[n-1]
@x:待插入元素
返回值:
无
*/
void BinInsert(int a[], int n,int x)
{
int i;
int low = 0;//查找范围,最左边元素的下标
int high = n-1;//查找范围,最右边元素的下标
int mid;//查找范围中间元素的下标
//step 1:用二分法找插入位置
while(low <= high)
{
mid = (low + high)/2;
if(x >= a[mid])
{
low = mid + 1;
}
else //x < a[mid]
{
high = mid - 1;
}
}
//high+1 就是x的插入位置
//step2: 插入操作(先挪后插入)
for(i = n - 1; i >= high+1; i--)
{
a[i+1] = a[i];
}
a[high+1] = x;
}
void insertSort(int a[] , int n)
{
int i,j;
for(i = 1; i < n; i++)
{
BinInsert(a,i,a[i]);
}
}
int main()
{
int a[N]={0};
int i;
for(i=0;i<N;i++)
{
scanf("%d",&a[i]);
}
insertSort(a,N);
for(i=0;i<N;i++)
{
printf("%d",a[i]);
}
printf("\n");
return 0;
}
3.折半插入排序的性能分析
(1)时间复杂度
一次BinInsert的时间T=Ts +Tm
其中 Ts 为一次二分查找的时间
二分查找插入位置时,都是“查找不成功(查找最坏)”的情况
so,每次查找比较次数为:1og2N
Ts=t1*log2N
Tm为一次移动元素的时间
根据插入位置,每次插入移动元素的个数,分为:
最好情况:移动一个
最坏情况:移动n个
平均情况:移动(n/2)
所以,我们根据移动元素的情形,把折半插入排序的时间复杂度分为三种情况:
最好情况:
O(nlog2N)
最坏情况:
O( n2)
平均情况:
O( n2)
(2)空间复杂度
O(1)
4.折半插入排序稳定性分析
折半插入排序 是 稳定的。
5.总结
折半插入排序相比直接插入排序只优化了查找插入位置的比较次数,移动元素的次数并没有解决。所以:
(1)当数据元素比较多(N值比较大)or
(2)移动元素的代价 小于 关键字比较时
采用折半插入排序比较有优势。