线性时间选择问题:
给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k小的元素。
1、随机划分线性选择
基本思想:
- 只对划分出的子数组之一进行递归处理。
- 子数组的选择与划分元和k相关。
#include <iostream>
#include <ctime>
using namespace std;
int a[] = {5, 7, 3, 4, 8, 6, 9, 1, 2};
template<class Type>
void Swap(Type &x, Type &y);
inline int Random(int x, int y);
template<class Type>
int Partition(Type a[], int p, int r);
template<class Type>
int RandomizedPartition(Type a[], int p, int r);
template<class Type>
Type RandomizedSelect(Type a[], int p, int r, int k);
int main() {
for (int i = 0; i < 9; i++) {
cout << a[i] << " ";
}
cout << endl;
cout << RandomizedSelect(a, 0, 8, 3) << endl;
}
template<class Type>
void Swap(Type &x, Type &y) {
Type temp = x;
x = y;
y = temp;
}
inline int Random(int x, int y) {
srand((unsigned) time(0));
int ran_num = rand() % (y - x) + x;
return ran_num;
}
template<class Type>
int Partition(Type a[], int p, int r) {
int i = p, j = r + 1;
Type x = a[p];
while (true) {
while (a[++i] < x && i < r);
while (a[--j] > x);
if (i >= j) {
break;
}
Swap(a[i], a[j]);
}
a[p] = a[j];
a[j] = x;
return j;
}
template<class Type>
int RandomizedPartition(Type a[], int p, int r) {
int i = Random(p, r);
Swap(a[i], a[p]);//随机获得的一个位置元素,与所要划分的子数组起始位置元素交换
return Partition(a, p, r);
}
template<class Type>
Type RandomizedSelect(Type a[], int p, int r, int k) {
if (p == r) {
return a[p];
}
int i = RandomizedPartition(a, p, r);//划分元位置i
int j = i - p + 1;//左子数组a[p:i]的元素个数
if (k <= j) {
return RandomizedSelect(a, p, i, k);
} else {
//由于已知道子数组a[p:i]中的元素均小于要找的第k小元素
//因此,要找的a[p:r]中第k小元素是a[i+1:r]中第k-j小元素。
return RandomizedSelect(a, i + 1, r, k - j);
}
}
注释:
1、利用随机函数产生划分基准,将数组a[p:r]划分成两个子数组a[p:i]和a[i+1:r],使a[p:i]中的每个元素都,不大于,a[i+1:r]中的每个元素。
2、计算a[p:i]中元素个数j=i-p+1。
3、如果k<=j,则a[p:r]中第k小元素在子数组a[p:i]中。
4、如果k>j,则第k小元素在子数组a[i+1:r]中,要找的a[p:r]中第k小元素是a[i+1:r]中第k-j小元素。
5、在最坏的情况下,找到最小元素时,总是在最大元素处划分,这是时间复杂度为O(n^2)。但平均时间复杂度与n呈线性关系,为O(n)。
2、利用中位数线性时间选择
问题分析:
- 如果能在线性时间内找到一个划分基准,使