按照题目描述,我们需要将插入排序中的5~7行,while循环实现的将“比A[i]大的数后移”的功能用二分查找的思路进行优化。2.3-5也有提到优化思路,即插入排序每一次循环结束后前面的i个数已经完成从小到大的排序,只需要将目标值A[j]与前i个数的中间值A[i/2]比较,就能知道A[j]应该在的区间,并减少一半的判断时间。
伪代码://_(:зゝ∠)_
BINARY_INSERTION_SORT ( A , L , R )//输入A[0..n],0,n
if R > 2……………………………………………………c1
BINARY_INSERTION_SORT ( A , L , R-1 )…………c2//R==2之前一直调用函数对前面的R-1个元素进行排序
key = A[ R ]………………………………………………c3//待插入的值给key
BINARY_INSERT ( A , L , R - 1 , key )………………c4//将A[ R ]插入前面已经排好的0~R-1序列中
//递归的插入排序主函数
BINARY_INSERT ( A , L , R , key )
if L > R……………………………………………………c5
A[ L] = key…………………………………………c6
return………………………………………………c7//若左溢出则将key赋给溢出之前的位置,若右溢出则赋值给L=R+1这个溢出位,即未排序的那个数的位置不动
else if key <= A[ ( L + R ) / 2]…………………………c8//若key值在二分点左侧则要将二分点右侧所有元素右移一位
for i = R downto ( L + R ) / 2……………………c9
A[ i + 1 ] = A [ i ]……………………………………c10
BINARY_INSERT ( A , L , ( L + R ) / 2 , key )……c11//继续找key该呆的位置
else BINARY_INSERT ( A , ( L + R ) / 2 , R , key )…………c12//若key值在二分点右侧则不做处理,继续迭代
//二分比较并移动较大项函数
鉴于计算过程太繁杂,手码的话要浪费大量时间,就直接给结果了_(:зゝ∠)_,其中k1~5都是用于替换c1~11组成的一次算式的常数。
BINARY_INSERTION_SORT函数的递归式为:
T1( n ) = k4 + T1( n - 1 ) + T2( n ),n>=2.
BINARY_INSERT函数的递归式为:
T2 ( n ) = k1 * n + k2 + T2 ( n - 1 ),n>1
T2 ( 1 ) = k3, n=1.
通式:(lg(n)代表以2为底,n的对数,书上有说明
T2( n ) = 2 * k1 * n + k2 * lg(n) - 2 * k1 +k3
T1( n ) = k1 * (n² + n - 2) + k2 * lg(n!) + k5 * ( n - 1 )//用斯特林公式将n!做近似处理,不懂的这里https://blog.csdn.net/SSYITwin/article/details/80111157
T1( n ) = k1 * n² + k2 * n *lg(n) + ( k1 - lg(e) *k2 + k5 ) *n +lg((2*π*n)½) -2 * k1 - k5
故将while替换后也不会对原插入排序的复杂度O(n²)造成多大影响,更不可能达到题目要求的O(n*lgn)。
但是这做法也做了对while的部分优化,优化了多少呢?有兴趣的同学请看下面。
将k1~k5的值代入原式(自己拿草稿纸写一下吧
k1= 0.5 * ( c9 + c10 + c11 )
k2= c5 + c8 + c9
k3= c5 + c6 + c7
k4= c1 + c2 + c3 + c4
k5= - k2 + k3 + k4
接下来将https://blog.csdn.net/gaint_moon_mystery/article/details/82289834这篇里的k1、k2的值也代入原式T(n)。(用书里第14~15页的代码与T(n)的通式比较也行。
对比两组代码中的相似部分,本文中n²的系数k1中包含了c10(自减)、c11(换位)、c12(调用自己),而书中n²的系数包含了c5(比较大小、判断是否溢出)、c6(换位)、c7(自减)。可见其中 自减 和 换位 两项没有被优化,而本文中的比较大小和判断是否溢出分别是c8(比较大小)、c5(判断是否溢出),他们分别属于,c8->(k2,k5),c5->(k2,k3,k5)。如果我们忽略小值,那么c8(比较大小)的时间复杂度为O(n*lgn),c5(判断是否溢出)的时间复杂度为O(n)。
如果把比较大小和判断是否溢出这两个判断语句拆开来算,那么这个改动对原插入排序排序算法的的优化点为,将“比较两数大小”由O(n²)降低到O(n*lgn),将“判断是否溢出”O(n²)降低到O(n),额外增添了“自己调用自己”复杂度为O(n²)。
全局优化为O(n²)-O(n*lgn)-O(n)。不知道“自己调用自己”这步的具体时长与另外两步有多大区别,好心前辈可以帮忙补充一下或指出我的错误,不然看起来好像还不如不优化_(:зゝ∠)_,谢谢大家,喜欢请点赞(>^ω^<)。