问题
设L是n个元素的集合,从L中选取第K小的元素,其中1<=K<=n。
解析
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_rank_k(int A[], int low, int high, int k)
{
int r_group = ceil((high - low + 1)*1.0 / r);
for (int i = 1; i <= r_group; ++i) {
sort(&A[low + (i - 1)*r], &A[(low + i*r - 1) > high ? high : (low + i*r - 1)]);
swap(A[low + i - 1], A[low + (i-1)*r + r / 2]);
}
sort(&A[low], &A[low + r_group]);
swap(A[low], A[r_group / 2]);
int cur = partition(A, low, high);
if (cur == k-1){
return A[cur];
}
else if (cur < k){
return select_rank_k(A, cur + 1, high, k);
}
else{
return select_rank_k(A, low, cur - 1, k);
}
}
分析
时间复杂度为O(n)
源码
#include <stdio.h>
#include <algorithm>
#include <math.h>
using namespace std;
int partition(int A[], int low, int high)
{
int pivot = A[low];
while (low < high){
while (low < high && A[high] >= pivot){
--high;
}
A[low] = A[high];
while (low < high && A[low] <= pivot){
++low;
}
A[high] = A[low];
}
A[low] = pivot;
return low;
}
int r = 5;
//A[low..high]
int select_rank_k(int A[], int low, int high, int k)
{
int r_group = ceil((high - low + 1)*1.0 / r);
//计算每个分组中值,存于A[]最前面
for (int i = 1; i <= r_group; ++i) {
sort(&A[low + (i - 1)*r], &A[(low + i*r - 1) > high ? high : (low + i*r - 1)]);
swap(A[low + i - 1], A[low + (i-1)*r + r / 2]);
}
sort(&A[low], &A[low + r_group]);
swap(A[low], A[r_group / 2]);
int cur = partition(A, low, high);
if (cur == k-1){
return A[cur];
}
else if (cur < k){
return select_rank_k(A, cur + 1, high, k);
}
else{
return select_rank_k(A, low, cur - 1, k);
}
}
int main(void)
{
int A[15] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
printf("%d\n", select_rank_k(A, 0, 3, 2));
return 0;
}