折半插入排序也属于插入排序的一种,相较于简单插入,对查找插入位置的过程做了改进。
分析
只说明一下查找插入点的过程,其他都与直接插入一致。
分别设置变量 low,high表示有序序列的低位端和高位端下标。每次将low和high的中位值(记为mid)处的元素值与待插入值进行比较。如果较大,则令high=mid-1,否则,则令low=mid+1。 循环这个过程,直到high<low。然后把待插入值插入high后面的位置(即low所表示的位置)。 想一下如果mid处的值和待插入处的值相等怎么办,如果处理不当就会引起不稳定的情况发生,但是这是可以避免的。假如两者值相等,前面介绍的方法是会执行"否则"那条分支,即让low=mid+1,这样就把查找的范围放到了大于mid的半区,结果必然是在mid的后面,这样不会出现不稳定的情况。如果把等于的情况加在第一条分支上面,则high=mid-1,这样查找的范围就放到了小于mid的半区,结果必然实在mid的前半区,如果有相等的值,就必然会引起不稳定的情况。
查找结束的时候的,high=low-1。待插入的位置也是low所指的位置。
复杂度分析
对于时间复杂度: 外出有一个循环n-1次,内层有两种操作,一个是查找插入点,需要log2n(最大的情况)次,另外一个是移动元素,需要n(最坏情况)次。如果目标序列顺序与原顺序一样,则是最好情况,第二种移动元素的操作就会空转,不会执行,但是第一种操作是都会执行的,记作O(nlog2n)。最好情况,刚好相反,序列顺序与目标排序序列刚好相反,则第二种操作满转,注意一下,这里由于n>log2n,所以直接取n就行了,记为O(n2)。
对于空间复杂度,不随排序规模增大而改变,所以是常量级别,记为O(1)。
代码
void half_insert(int *array, int n)
{
for(int i=1;i<n;++i)
{
int temp=array[i];
int low=0, high=i-1;
while(low<=high)
{
int m=(low+high)/2;
if(array[m]>temp)
high=m-1;
else
low=m+1;
}
int j=i;
while(j>low)
{
array[j]=array[j-1];
--j;
}
array[low]=temp;
}
}