一、需求分析
0.问题描述
元素选择问题的一般提法是:给定线性序集中n个元素和一个整数k (1≤k≤n), 要求找出这n个元素中第k小的元素,即如果将这n个元素依其线性序排列时,排在第k个位置的元素即为要找的元素。当k=1时,就是要找的最小元素:当k=n时, 就是要找最大元素;当k=(n+1)/2 时,称为找中位数。
1.问题分析
- 对于选择问题而言,用拟中位数作为划分基准可以保证在最坏的情况下用线性时间完成选择。
- 如果只简单地用待划分数组的第一个元素作为划分基准,则算法的平均性能较好,而在最坏的情况下需要O(n^2)计算时间。
- 舍伍德选择算法则随机地选择一个数组元素作为划分基准,这样既保证算法的线性时间平均性能,又避免了计算拟中位数的麻烦。
3.输出数据
分析并阐述要输出的信息和数据(结果)
4.测试样例设计
针对问题的功能,设计5组有代表性的测试样例,说明每组测试样例的测试目的
二、算法设计与分析
1.算法的基本思想
类似于快排的一种思想。
- 随机选择一个基准值pivot,将基准值与范围左侧元素交换位置。
- 以基准值为轴进行划分,先从右往左找到一个位置j的元素小于pivot,然后从左往右找到一个位置i的元素大于pivot,交换两个元素的位置。
- 当i==j时停止交换,这时j的左侧元素小于等于pivot,右侧元素大于等于pivot、
- 因为j处的元素肯定小于等于pivot,将L与j处元素交换位置,j位置的元素就是这个序列里面第 j - L+1小的元素。
- 如果j - L +1==k,则问题解决
- 否则判断k与j - L +1的相对大小,当j - L +1< k时继续对右侧的序列重复以上过程,否则对左侧序列重复以上过程,直到找到满足条件的j - L +1
2.输入和输出的格式
- 从文件输入,输出到文件。
- 输入第一行是元素个数n和要求的元素的位置k
- 第二行是n个元素的值
3.算法的具体步骤
4.算法的时空分析
①时间复杂度:
-
①时间复杂度:
划分基准是随机的。可以证明,当用算法Select对含有n个元素的数组进行划分时,划分出的低区子数组中含有一个元素的概率为2/n,含有i个元素的概率为1/n (i=2, 3,.,. n-1)。
设T(n)是算法Select作用于一个含有n个元素的输入数组上所需的期望时间的上界,且T(n)是单调递增的。在最坏情况下,第k小元素总是被划分在较大的子数组中。得到关于T(n)的递归式
-
解递归式得到结果:O(n)
②空间复杂度:
存储元素的值,O(n)
②空间复杂度:
存储元素的值,O(n)
三、测试结果
5 2
5 9 1 4 610 8
19 2 56 1 3 5 3 2 7 9
全部代码:
#include <iostream>
#include <time.h>
#include <stdlib.h>
#include <fstream>
using namespace std;
ifstream in("input.txt");
ofstream out("output.txt");
//内联交换函数
template <class Type>
inline void Swap(Type& a, Type& b){
Type temp = a;
a = b;
b = temp;
}
template<class Type>
Type select(Type a[], int l, int r, int k){
while (true){
if (l >= r){
return a[l];
}
//随机选择划分基准
int i = l, j = l + rand() % (r - l + 1);
Swap(a[i], a[j]);
j = r + 1;
Type pivot = a[l];
//以划分基准为轴做元素交换
while (true){
while (i<r&&a[++i] < pivot);
while (j>l&&a[--j] > pivot);
if (i >= j){
break;
}
Swap(a[i], a[j]);
}
//如果最后基准元素在第k个元素的位置,则找到了第k小的元素
if (j - l + 1 == k){
return pivot;
}
//a[j]必然小于pivot,做最后一次交换,满足左侧比pivot小,右侧比pivot大
a[l] = a[j];
a[j] = pivot;
//对子数组重复划分过程
if (j - l + 1 < k){
k = k - (j - l + 1 );//基准元素右侧,求出相对位置
l = j + 1;
}else{//基准元素左侧
r = j - 1;
}
}
}
template <class Type>
Type Select(Type a[],int n,int k){
if(k<1||k>n){
printf("Index out of bounds\n");
exit(0);
}
return select(a,0,n-1,k);
}
int main(){
int n,k,r;
while(in>>n){
in>>k;
int a[n];
for(int i=0;i<n;i++)
in>>a[i];
r=Select<int> (a,n,k);
out<<r<<'\n';
}
in.close();
out.close();
return 0;
}