在一个数组中找到第k小的数(线性时间选择)
在这一部分,我们讨论的题目为元素选择问题。这类题目的一般问法为:给定线性序集中n个元素和一个整数k,1 <= K <= n,要求找出这n个元素中第k小的元素,如(1,4,3,2,5)中第一小的元素就是1,第5小的元素就是5,第2小的元素就是2。
在某些特殊情况下,很容易设计出解选择问题的线性时间算法。如:当要选择最大元素或最小元素时,显然可以在O(n)时间完成。如果k <= n/logn,通过堆排序算法可以在O(n + klogn) = O(n)时间内找出第k小元素。当k >= n - n/logn时也一样。
一般的选择问题,特别是中位数的选择问题似乎比最小(大)元素要难。但实际上,从渐近阶的意义上,它们是一样的。也可以在O(n)时间完成。
下面我们用两种方法进行求解。
第一种:分治算法RandomizedSelect
思想:调用了随机划分函数RandomizedPartition,所以这个分治算法也是一个随机化的算法。
通过将其划分,我们逐渐可以缩小查找的范围进行查找。
时间复杂度:一般情况下为O(n),最坏情况下,数字有序且找最大的数,则为O(n)
代码:
#include<windows.h>
#include<iostream>
#include<assert.h>
#include<time.h>
#include<vector>
#include<limits.h>
using namespace std;
template<class Type>
int Partition(Type *ar,int left,int right)
{
int i = left, j = right;
Type tmp = ar[i];
while(i<j)
{
while(i<j && ar[j] > tmp) --j;
if(i<j) { ar[i] = ar[j];}
while(i<j && ar[i] <= tmp) ++i;
if(i<j) { ar[j] = ar[i];}
}
ar[i] = tmp;
return i;
}
template<class Type>
int RandPartition(Type *ar,int left,int right)
{
//Sleep(800);
srand(time(NULL));
int pos = (rand() % (right - left + 1)) + left;
swap(ar[pos],ar[left]);
return Partition(ar,left,right);
}