选择第k小元素
设 L 是 n 个元素的集合,从 L 中选取第 k 小的元素,其中 1<=k<=n.这里
的第 k 小元素是指,当 L 按从小到大排好序之后,排在第 k 个位置的元素。
算法思想
解决该问题用到的是分治思想。
下面引用老师的分析
算法思想:
- k=|S1|+1,m*就是所要找的第k小的数;以m为划分标准后,比m小的有|S1|个,如果恰巧,k=|S1|+1,则m就是所要找到第k小的数
- k<=|S1|,归约为在S1中找第k1小的子问题,k1在子问题中的相对位置不变,即k1=k;
- k>|S1|+1,归约为在S2中找k2位置的子问题,k2相对于S2子问题和k相对于S的关系,即k2=k-|S1|-1。(在S中找k,就是在S2中找k2)
核心伪代码
int Select(int A[], int left, int right, int k)
{
int p = right - left;//计算数组大小
if (p < 5)//规模小,直接排序
{
sort(A + left, A + right);
return A[left + k - 1];
}
int q = p / 5;//分为q组
int* M = new int[q];
for (遍历分组)
{ //排序后得到中位数
}
//对中位数数组进行排序得到Mid
for (遍历数组)
{
if (元素小于mid)
存入A1数组
else if (元素等于Mid)
存入A2
else//其余情况
存入A3
}
if (若小于mid的数超过k个)
return Select(A1, 0, count1, k);
if (若小于mid的数加上等于mid的数超过k个)
return Mid;
//其余情况
return Select(A3, 0, count3, k - count1 - count2);
}
算法分析
下面引用老师的分析
-
假设n是5的倍数,且n/5是奇数,即n/5=2r+1,于是又|A|=|D|=2r,|B|=|C|=3r+2,n=10r+5。
-
如果A,D的元素都小于m*,那么它们都加入至S1中,且下一步算法又在这个子问题上递归调用,这对应了归约后子问题的规模的上界,也正好是时间复杂度最坏的情况。
-
子问题规模为:|A|+|C|+|D|=7r+2=7*(n−5)/10 + 2=7*(n/10)−1.5<7*n/10
表明子问题规模不超过原问题规模的7/10
W(n)<=W(n/5)+W(7n/10)+tn其中,W(n/5)为查找m* 的时间,总规模 n 中选出 n/5 个数来找中位数
tn 是构造中位数集合,以及A & D和m* 进行比较的时间开销,t 是某个常数
W(n)<=tn+0.9tn+(0.9^2)tn+…=tn(1+0.9+0.92+…)=O(n)
所以最后推导出算法时间复杂度为O(n)