分治算法:线性时间选择第k大小的数字 && STL函数:nth_element()

(一)手动实现线性时间选择第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大的数字。




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值