(一)手动实现线性时间选择第i大小数字的算法
和快速排序类似,选择第k大小的数字可以用分治算法来解决。
令函数 int f(int a[], int s, int e, int i) 表示 a[s]......a[e] 这一段序列中的第 i 大小的数字。该怎么实现这个函数呢?
如果我们调用快速排序中的选择主元函数 select 找到一个主元 a[m](下标为m的元素)。那么主元在 a[s]......a[m]中就是第 m - s + 1大的元素。
令 k = m - s + 1
如果 k == i
那么我们所找的第i大小的元素就是 a[m]。
如果 k > i
那么我们所找的第i大小的元素在前半段,也就是 a[s]......a[m - 1]这一段。递归调用 f(a, s, m - 1, i)即可。
如果 k < i
那么我们所找的第i大小的元素在后半段,也就是 a[m + 1]......a[e]这一段。递归调用 f(a, m + 1, e, i - k)即可。
可以知道通过 O(n) 时间的选择主元算法,我们可以将问题的规模减少一半。
所以和快速排序一样,时间复杂度为 O(nlogn)
代码如下:
#include <cmath>
#include <ctime>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX_N 1005
#define EPS 1e-10
using namespace std;
typedef long long int ll;
/*********************************************************
参数:a 操作的数组。s 区间的左端点。e 区间的右端点。
功能:在a[s]......a[e]这一段数字中选择一个主元,满足
主元左边的数字都小于主元,右边的数字都大于主元。
**********************************************************/
int select(int a[], int s, int e)
{
int j = s, i = s + 1;
while (i <= e)
{
if (a[i] > a[s])
i++;
else swap(a[i++], a[++j]);
}
swap(a[s], a[j]);
return j;
}
/*********************************************************
参数: a 操作的数组。s 区间的左端点。e 区间的右端点。
i 要选择第i大的数字。
功能: 在a[s]......a[e]这一段数字中找到第i大的数字,
并将其返回。
**********************************************************/
int select_element(int a[], int s, int e, int i)
{
if (s == e) // 特判
return a[s];
int m = select(a, s, e);
int k = m - s + 1; // 主元在a[s]......a[e]这一段是第k大的数字
// 分三种情况:1.第i大的数字就是主元a[m]。2.第i大的数字在主元的左边。3.第i大的数字在主元的右边。
if (i == k)
return a[m];
else if (i < k)
select_element(a, s, m - 1, i);
else
select_element(a, m + 1, e, i - k);
}
int main()
{
//freopen("1.txt", "r", stdin);
//freopen("2.txt", "w", stdout);
int a[5] = {1 ,3, 0, 7, 5};
cout << select_element(a, 0, 4, 3) << endl;
return 0;
}
(二)STL函数:nth_element()
void std::nth_element(_RAIter, _RAIter, _RAIter)
第一个参数是序列的起始地址。
第二个参数是找到第i大小数字后,要将其存放的地址。也就是这个地址在函数运行完之后会保存着我们想要的答案:第i大小的数字。
第三个参数是序列的结束地址的下一个地址。
int a[5] = {1 ,3, 0, 7, 5};
nth_element(a, a + 2, a + 5);
cout << a[2] << endl;
注意:这里 a[2] 是指第3大的数字。