线性选择算法
选择问题定义,实际上所有处理均可以推广到集合中包含重复数值的情形。
输入:一个包含n个(不同的)数的集合A和一个数i,1<=i<=n。
输出:元素x属于A,它恰大于A中其他的i-1个元素。
1、最大值最小值
针对一个序列取得最大和最小值均需要n-1次比较。这是一个下限,确定最大值或者最小值的算法可以看作各个元素之间一场锦标赛,每次比较都是一场比赛,两个元素中较小的或者较大的获胜,除了最终的最大值和最小值,所有其他元素都需要输一次,所以n-1次是必须的。
接下来是一些比较有意思的问题,比如同时找出最小值和最大值,当然可以n-1次比较找出最大值,然后n-2次比较找出最小值,不过还是有比这个更好一点的算法,把元素两两分组,然后比较产生一个较大的值和较小的值,然后较大的值中产生最大值,较小的值中产生最小值,此时需要比较操作的次数至多3|_n/2_|。
还有一个比较问题是同时找出最大第二大或者最小次小元素的比较次数,简单的当然是2n-3,不过也有一个分组的方法能够达到n+lgn-2的比较次数。比较方法如下:
上面已经说明了,找出最值最少的比较次数n-1,所以上面寻找的方法也是n-1次,不信可以累计求和,不过这样求最值的过程中最值上来的时候有一条路径被记录,这条路径的长度为lgn,找出次大值或者次小值直接在这个路径上寻找就只需要lgn-1的比较次数。
2、线性时间选择
上面只是抛砖引玉,接下来才是选择问题的具体算法,分别是一个期望线性时间和最坏情况线性时间的选择算法。
期望选择时间伪代码(算法里的partition和快速排序里的partition一样的方法)
RANDOMIZED-SELECT(A,p,r) if p = r then retrun A[p] q= RANDOMIZED-PARTITION(A,p,r) k= q-p+1 if i = k then return A[q] else if i<k return RANDOMIZED-SELECT(A,p,q-1,i) else return RANDOMIZED-SELECT(A,q+1,r,i-k)
RANDOMIZED-PARTITION(A,p,r) i=RANDOM(p,r) exchange A[r],A[i] return PARTITION(A,p,r) PARTITION(A,p,r) x=A[r] i=p-1 for j=p to r-1 do if A[j]<=x i++ exchange A[i],A[j] exchange A[i+1],A[r] return i+1
上面方法的期望时间是线性的,数学证明比较复杂就省略了。
最坏情况线性时间
上面期望时间线性是因为可能存在某种最坏的情况,每次分割元的位置都是导致一边没有元素,这样就不能够达到线性了,接下来的一些预先工作就是避免这种情况发生。
算法SELECT通过执行下列步骤来确定一个有n个元素的输入数组中的第i个小的元素。
1、将输入数组的n个元素划分为n/5组,每组5个元素,且至多有一个组由剩下的n mod 5个元素组成。
2、寻找n/5个组中每一组的中位数。(方法首先对每组中的元素进行插入排序,然后从排序过的序列中选出中位数)
3、对第2步中找出的n/5个中位数,递归调用SELECT找出其中位数x。(如果有偶数个中位数,根据约定,x是下中位数。)
4、利用修改过的PARTITION过程,按中位数的中位数x对输入数组进行划分。让k比划分低区的元素多1,所以x是第k小的元素,并且有n-k个元素在划分的高区。
5、如果i=k,则返回x,否则,如果i<k,则在低区递归调用SELECT以找出第i小的元素,如果i>k,则在高区找第(i-k)个最小元素。
图中箭头指向表示大的数值指向小的数值,所以根据图可以看出,在x的右边,每一个包含5个元素的组中至少有3个元素大于x,x的左边,每一组中至少有3个元素小于x。(保证x分割一边必定有元素存在)
图中显示的中位数的中位数x的位置,每次选取x作为划分的好处是能够保证必定有一部分在x的一边。
所以算法最坏情况的递归公式可以写成:
使用替换法可以得出T(n)<=cn。
以上内容参考算法导论,转载请注明出处。