以下程序基于函数 int select_kth_smallest(list q, int k) 实现 :返回向量q中第k最小元的函数
基于冒泡排序思想,暴力求解:
基本思路:要求找出第k个最小元素,可以通过在序列中遍历k次,每次找出最小的,并放在序列头。类似泡泡一样,找出第k个大的泡泡(bubble)
伪代码:
int select_kth_smallest(list q, int k){
intput: q is a list, k is the number of which the kth smallest in list
output: return the value of the k largest number in list
size <- q.size
for i <- 0:k-1 do
min <- q[i]
index <- i
for j <- i+1 : size-1 do
if min > q[j] do
min <- q[j]
index <- j
endif
endfor
q[index] <- q[i]
q[i] <- min
endfor
return q[k-1]
}
代码:
int select_kth_smallest(vector q, int k){
int size = q.size();
for(int a=0; a q[b]){
min = q[b];
index = b;
}
q[index] = q[a];
q[a] = min;
}
return q[k-1];
}
算法复杂度分析:
由于都是在原地实现,所以空间复杂度为O(n)
寻找第k小元素,总共遍历了k次序列。T = n + (n-1) + (n-2) + ...... + (n-k-1) = kn - (1+2+...+k-1) = O(nk)
基于快速排序,高效求解
基本思路:每次以序列第一个元素作为中间数mid,将序列中小于等于mid的放在mid左边,反之放在mid右边。不妨设小于等于mid的个数为i,若k < i,则对左边序列递归做相同操作;若k > i, 则对右边序列递归寻找序列中第k-i小的元素;若k = i,则mid即为第k小的元素
伪代码:
int select_kth_smallest(list q, int k){
intput: q is a list, k is the number of which the kth smallest in list
output: return the value of the k largest number in list
bot <- 0
top <- size of q
while bot < top do
index <- q[bot]
left <- bot
right <- bot
while right < top do
if q[right] < index do
left <- left + 1
temp <- q[right]
q[right] <- q[left]
q[left] <- temp
right <- right + 1
endwhile
q[left] <- q[bot]
q[bot] <- index
if left + 1 < k do
bot <- left + 1
else if left + 1 > k do
top = left
else do
return q[left]
endif
return -1
}
代码:
int select_kth_smallest(vector q, int k){
int bot = 0, top = q.size();
while(bot < top){
int left = bot, right = bot, index = q[bot];
while(right < top){
if(q[right] < index){
left++;
int temp = q[left];
q[left] = q[right];
q[right] = temp;
}
right++;
}
/*
for(int a=0; a k)
top = left;
else
return q[left];
}
return -1;
}
算法复杂度分析:
空间复杂度:原地操作,O(n)
时间复杂度:由于将序列第一个元素作为mid来分开序列,具有一定的随机性。所以我们只能考虑极限情况,最坏情况时,即每次选的都是最大值或者最小值,则需要进行k次循环,T = O(n^2)。
BFPRT算法描述:
从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分 析,BFPRT可以保证在最坏情况下仍为线性时间复杂度。该算法的思想与快速排序思想相似,当然,为使得算法在最坏情况下,依然能达到o(n)的时间复杂 度,五位算法作者做了精妙的处理。
算法步骤:
1. 将n个元素每5个一组,分成n/5(上界)组。
2. 取出每一组的中位数,任意排序方法,比如插入排序。
3. 递归的调用selection算法查找上一步中所有中位数的中位数,设为x,偶数个中位数的情况下设定为选取中间小的一个。
4. 用x来分割数组,设小于等于x的个数为k,大于x的个数即为n-k。
5. 若i==k,返回x;若i
终止条件:n=1时,返回的即是i小元素。
伪代码:
int select_kth_smallest(list q, int k){
intput: q is a list, k is the number of which the kth smallest in list
output: return the value of the k largest number in list
bot <- 0
top <- the size of q
while bot < top do
left <- bot
right <- bot
index <- choose_mid(bot, top)
while right < top do
if q[right] < index do
left <- left + 1
temp <- q[right]
q[right] <- q[left]
q[left] <- temp
endif
right <- right + 1
endwhile
if left + 1 < k do
bot <- left + 1
else if left + 1 > k do
top = left
else do
return q[left]
endif
endwhile
return -1
}
int choose_mid(q, left, right){
input: q is a number list, left and right are the indexes of q indicating the range of choosing midian
output: the median in range of (left, right)
v <- [] // create a empty vector
while left + 5 < right do
mid <- selection(q[left : left+5])
push mid in v
left <- left + 5
endwhile
mid <- selection(q[left : right])
push mid in v
return selection(v)
}
int selection(v){
input: v is a number vector
output: return the midian in v
size <- the size of v
for a <- 1:size-1
b <- a
temp <- v[b]
while b > 0 and temp < v[b-1] do
v[b] <- v[b-1]
b <- b - 1
v[b] <- temp
index <- (size-1) // 2
return v[index]
}
代码:
int select_kth_smallest(vector q, size_t k);
int choose_mid(vector& q, int left, int right);
int selection(vector v);
int select_kth_smallest(vector q, size_t k){
int bot = 0, top = q.size();
while(bot < top){
int left = bot, right = bot, i;
int index = choose_mid(q, bot, top);
while(right < top){
if(q[right] < index){
int temp = q[left];
q[left] = q[right];
q[right] = temp;
left++;
}
if(q[right] == index)
i = right;
right++;
}
q[i] = q[left];
q[left] = index;
/*
for(int a=0; a k)
top = left;
else
return index;
}
return -1;
}
int choose_mid(vector& q, int left, int right){
vector v;
while(left+5 < right){
int mid = selection(vector(&q[left], &q[left+5]));
v.push_back(mid);
left += 5;
}
int mid = selection(vector(&q[left], &q[right]));
v.push_back(mid);
return selection(v);
}
int selection(vector v){
int size = v.size();
for(int a=1; a0 && temp
算法分析:
在第三步选出x时,大于x的元素至少有3n/10 - 6个,在最差的情况下,第五步递归作用的元素个数是7n/10 + 6.可得递归表达式如下:
T(n) <= T(n/5) + T(7n/10 + 6) + O(n)
比起算法二,消除了中间数mid选取的随机性干扰,使得在最差的情况下也是线性复杂度O(n)。