线性时间选择

来源:互联网 发布:sql工具 编辑:程序博客网 时间:2024/05/01 15:02

给定n个元素和1个k,1<=k<=n,要求找出这n个元素中第k小的元素。

下面讨论解一般的选择问题的分冶算法,实际是模仿快速排序算法,不同的是,只对划分出的子数组之一进行递归处理。


#include <iostream>#include <stdio.h>#include <time.h>using namespace std;int randomSelect(int a[], int p, int r, int k);int randomPartition(int a[], int p, int r);int myPartition(int a[], int p, int r);int main(){int a[10] = {5, 8, 50, 47, 63, 12, 30, 19, 9, 25};int m = randomSelect(a, 0, 9, 5);cout << m;}int randomSelect(int a[], int p, int r, int k){if(p == r) return a[p];int i = randomPartition(a, p, r);int j = i - p + 1;if(k <= j) return randomSelect(a, p, i, k);else return randomSelect(a, i + 1, r, k - j);}int randomPartition(int a[], int p, int r){srand(time(0));int i = p + rand() % (r - p + 1);int temp = a[i];a[i] = a[p];a[p] = temp;return myPartition(a, p, r);}int myPartition(int a[], int p, int r){int i = p, j = r + 1;int x = a[p];//将<x的元素交换到左边//将>x的元素交换到右边while(1){while(a[++i] < x && i < r);while(a[--j] > x);if(i >= j) break;int temp = a[i];a[i] = a[j];a[j] = temp;}a[p] = a[j];a[j] = x;return j;}

randomSelect最坏情况需要Ω(n^2)。但平均性能很好。


下面讨论类似于上算法, 但最坏情况下用O(n)时间就完成的算法。


如果能在限行时间内找到一个划分基准,使得按这个基准所划分出的两个子数组的长度都至少为原数组商都的m倍(0<m<1),那么久可以在最坏情况下用O(n)时间完成选择。例如,m=9/10,算法递归调用所产生的子数组的长度至少缩短1/10,所以在最坏情况下需要时间T(n)<=T(9n/10)+O(n)。可得T(n) = O(n)


划分基准:

(1)将n个输入元素划分成n/5(取上界),每组5个元素,只可能有一个组不是5个元素。用任一种排序算法,将每组中的元素排好,并取出每组的中位数,共n/5(取上界)。

(2)递归调用算法来找出这n/5(取上界)个元素的中位数。如果n/5是偶数,就找他的两个中位数较大的一个,以这个元素作为划分基准。

在这种情况下,找出的基准x至少比3(n-5)/10(上界)个元素大,因为在每组中有两个元素小于本组的中位数,而n/5个中位数中又有(n-5)/10个小于基准x,同理,x也至少比3(n-5)/10个元素少,。而当n>=75时,3(n-5)/10>=n/4,所以按此基准划分所得的两个子数组的长度都至少缩短1/4


#include <iostream>using namespace std;int mySelect(int a[], int p, int r, int k);int myPartition(int a[], int p, int r);void bubble(int a[], int first,int end);int main(){int a[10] = {5, 8, 50, 47, 63, 12, 30, 19, 9, 25};int m =mySelect(a, 0, 9, 5);cout << m;}int mySelect(int a[], int p, int r, int k){if(r - p < 5){bubble(a, p, r);return a[p + k - 1];}//将a[p+5*i]至a[p+5*i+4]的第三小元素与a[p+i]交换位置//找中位数的中位数,for(int i = 0;i <= (r - p - 4)/5;i++){int s = p + 5 * i, t = s + 4;for(int j = 0;j < 3;j++)  bubble(a, s, t - j);int temp = a[p+1];a[p+1] = a[s+2];a[s+2] = temp;}int x = mySelect(a, p, p+(r-p-4)/5, (r-p+6)/10);int i = myPartition(a, p, r), j = i-p+1;if(k <= j) return mySelect(a, p, i, k);else return mySelect(a, i+1, r, k-j);}void bubble(int a[], int first,int end) //冒泡排序{    for(int flag=first;flag<end;flag++)for(int i=end;i>flag;i--)if(a[i]<a[i-1]){     int t=a[i];a[i]=a[i-1];a[i-1]=t;}}int myPartition(int a[], int p, int r){int i = p, j = r + 1;int x = a[p];//将<x的元素交换到左边//将>x的元素交换到右边while(1){while(a[++i] < x && i < r);while(a[--j] > x);if(i >= j) break;int temp = a[i];a[i] = a[j];a[j] = temp;}a[p] = a[j];a[j] = x;return j;}

最坏情况时间为T(n)=O(n)

另外,除了5和75的组合外,还有其他选择。

原创粉丝点击