java 线性时间选择_经典算法总结之线性时间做选择

问题:

输入:一个包含n个(不同的)数的集合A和一个数i, 1 <= I <= n。

输出:元素x∈A, 它恰大于A中其他的I – 1个元素(即求第k小数)。

本博文中寻找最大的K个数(TOP K算法)这篇文章也用了本文中的算法,大家可以参考。

三种算法:

1、 直接排序,输出数组第i个元素即可, 时间复杂度为O(nlgn)

2、 这种算法,利用“快排的或者类似二分”的思想,每次以枢纽为界,分两边,每次只需处理一边即可(抛弃另一边),平均情况下的运行时间界为O(n),这种算法以期望时间做选择。《算法都论》里是,在分治时用随机数来选取枢纽(算法导论中伪代码见图),好吧,这是理论上的算法,它没有考虑实际产生随机数的开销,事实上,效率一点也不高,已经测试过,产生随机数花费的开销真的很大,后边我用更快的三数中值又实现了一遍,思想是一样的,只是效率提高了。

%E7%AC%ACk%E5%B0%8F%E9%9A%8F%E6%9C%BA.jpg

C++完整代码:

#include

#include

#include

using namespace std;

int partition(vector &A,int p,int r){

int x = A[r];

int i=p-1;

int temp;

for(int j = p;j

if(A[j]<=x){

++i;

swap(A[i],A[j]);

}

}

swap(A[i+1],A[r]);

return i+1;

}

inline int Random(int low, int high) {

return (rand() % (high - low + 1)) + low;

}

int Randomized_Partition(vector &kp, int low, int high) {

int i = Random(low, high);

swap(kp[high], kp[i]);

return partition(kp, low, high);

}

void randomized_quickSort(vector &A,int p,int r){

if(p

int q = Randomized_Partition(A,p,r);

randomized_quickSort(A,p,q-1);

randomized_quickSort(A,q+1,r);

}

}

int randomized_select(vector A,int p,int r,int i){

if(p==r)

return A[p];

if(p>r) return -1;

int q = Randomized_Partition(A,p,r);

int k = q-p+1;

if(i==k)

return A[q];

else if(i

return randomized_select(A,p,q-1,i);

else return randomized_select(A,q+1,r,i-k);

}

void main(){

int a[10] = {9,10,8,7,6,5,4,3,2,1};

vector A(a,a+10);

cout<

}

3、 第三种算法以最坏情况线性时间做选择,最坏运行时间为O(n),这种算法基本思想是保证每个数组的划分都是一个好的划分,以5为基,五数取分,这个算法,算法导论没有提供伪代码,额,利用它的思想,可以快速返回和最终中位数相差不超过2的数,这样的划分接近最优,基本每次都二分了(算法导论中步骤见图)

%E6%B1%82%E7%AC%ACk%E5%B0%8F%E6%95%B0%E4%BA%94%E5%88%86%E4%BD%9C%E6%B3%95.jpg

/*利用中位数来选取枢纽元,这种方法最坏情况下运行时间是O(n)

这里求的中位数是下中位数算法导论里没有伪代码,

写起来很麻烦注意这里的查找到的中位数,

并不是真正意义上的中位数而是和真正中位数相差不超过2的一个数开始以为我写错了

,又看了算法导论,应该就是这个意思返回的是[x - 1, x + 2]的一个数,中位数是x从下边的输出中也可以看出:*/

#include

#include

using namespace std;

const int maxn = 14;//kp -> size

const int maxm = maxn / 5 + 1;//mid -> size

int kp[maxn];int mid[maxm]; //插入排序

void InsertionSort(int kp[], int n) {

for (int j, i = 1; i < n; i++) {

int tmp = kp[i];

for (j = i; j > 0 && kp[j - 1] > tmp; j--) {

kp[j] = kp[j - 1];

}

kp[j] = tmp;

}

} //查找中位数, 保证每一个划分都是好的划分

int FindMedian(int kp[], int low, int high) {

if (low == high) {

return kp[low];

}

int index = low;//index初始化为low

//如果本身小于5个元素,这一步就跳过

if (high - low + 1 >= 5) { //储存中位数到mid[]

for (index = low; index <= high - 4; index += 5) {

InsertionSort(kp + index, 5);

int num = index - low;

mid[num / 5] = kp[index + 2];

}

} //处理剩下不足5个的元素

int remain = high - index + 1;

if (remain > 0) {

InsertionSort(kp + index, remain);

int num = index - low;

mid[num / 5] = kp[index + (remain >> 1)];//下中位数

}

int cnt = (high - low + 1) / 5;

if ((high - low + 1) % 5 == 0) {

cnt--;//下标是从0开始,所以需要-1

}//存放在[0…tmp]

if (cnt == 0) {

return mid[0];

} else {

return FindMedian(mid, 0, cnt);

}

} int Qselect(int kp[], int low, int high, int k) {

int pivotloc = FindMedian(kp, low, high); //这里有点不一样,因为不知道pivotloc下标,所以全部都要比较

int i = low - 1, j = high + 1;

for (; ;) {

while (kp[++i] < pivotloc) {}

while (kp[--j] > pivotloc) {}

if (i < j) swap(kp[i], kp[j]);

else break;

} int num = i - low + 1;

if (k == num) return kp[i];

if (k < num) {

return Qselect(kp, low, i - 1, k);

} else {

return Qselect(kp, i + 1, high, k - num);

}

}

int main() {

int kp[maxn] = {10, 14, 8, 11, 7, 1, 2, 13, 3, 12, 4, 9, 6, 5};

for (int i = 0; i < maxn; i++) {

printf("中位数是: %d\n", FindMedian(kp, 0, maxn - 1));

printf("第%d小的数是: ", i + 1);

cout << Qselect(kp, 0, maxn - 1, i + 1) << endl << endl;

}

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值