您的位置:首页 > 其它

线性时间选择问题-第k小(大)问题-递归与分治

2018-03-28 19:22 435 查看
问题:给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素。在线性时间内O(n)?
解析:
定义查找第k小元素的算法为 Select(Type a[], int p, int r, int k) 。
将n个输入元素划分成n/5个组,每组5个元素,只可能有一个组不是5个元素。
用任意一种排序算法,将每组中的元素排好序,并取出每组的中位数,共n/5个。
递归调用算法select来找出这n/5个元素的中位数。

如果n/5是偶数,就找它的2个中位数中较大的一个。以这个元素作为划分基准。
伪代码:Type Select(Type a[], int p, int r, int k)
{
if (r-p<75) {
用某个简单排序算法对数组a[p:r]排序;
return a[p+k-1];
};
for ( int i = 0; i<=(r-p-4)/5; i++ )
//将元素每5个分成一组,分别排序
BubbleSort(a,p+5*i,p+5*i+4);
//将该组中位数与a[p+i]交换位置,即: 将a[p+5*i] 至
a[p+5*i+4]的第3小元素与a[p+i]交换位置;
Swap(a[p+5*i+2],a[p+i]);
//找中位数的中位数,r-p-4即上面所说的n-5
Type x = Select(a, p, p+(r-p-4)/5, (r-p-4)/10);
int i=Partition(a,p,r, x),
j=i-p+1;
if (k<=j) return Select(a,p,i,k);
else return Select(a,i+1,r,k-j);
} 代码:#include <bits/stdc++.h>
using namespace std;
int Partition(int *a, int p, int n, int beginval)
{
int temp, beginpos;
for(int i = 0; p <= n - p + 1; ++i)
if(a[p + i] == beginval)
{
beginpos = p + i;
break;
}
temp = a[p];
a[p] = a[beginpos];
a[beginpos] = temp;
temp = a[p];
while(p < n)
{
while(a
>= temp && p < n) --n;
a[p] = a
;
while(a[p] <= temp && p < n) ++p;
a
= a[p];
}
a[p] = temp;
return p;
}
int Select(int *a, int p, int n, int k)
{
int temp;
if(n - p < 75)
{
sort(a + p, a + n);
return a[p + k - 1];
}
else
{
int ArrNum = (n - p + 1) / 5, i;
if(5 * ArrNum != n - p + 1)
++ArrNum;
for(i = 0; i < ArrNum - 1; ++i)
{
sort(a + p + i * 5, a + p + i * 5 + 4);
temp = a[p + i];
a[p + i] = a[p + i * 5 + 2];
a[p + i * 5 + 2] = temp;
}
sort(a + p + i * 5, a + n);
int midval = Select(a, p, p + ArrNum, ArrNum / 2);
int midpos = Partition(a, p, n, midval);
int ranking = midpos - p + 1;
if(k <= ranking)
return Select(a, p, midpos, k);
else
return Select(a, midpos + 1, n, k - ranking);
}
}
int main()
{
int a[100];
int n, k;
scanf("%d %d", &n, &k);
for(int i = 0; i < n; ++i)
scanf("%d", &a[i]);
int val = Select(a, 0, n, k);
printf("%d\n", val);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: