线性选择算法

问题描述:

在给定线性集中有n个元素,要求找出这n个元素中第k(1<=k<=n)小的元素。给定的线性集是无序的。要求在线性时间内完成,线性时间的要求是指在最坏情况下也要保证在O(n)时间内完成选择。

设计思路:

我们采用一种类似于快速排序的划分的方法。最开始我们将所有的元素五个一组,分为⌈n/5⌉个组(最后剩余不够5个也算一组),然后对于每组无论采用哪种排序算法进行排序,找出其中位数,最后找出共⌈n/5⌉个中位数。递归调用该算法找出这⌈n/5⌉个中位数的中位数(如果⌈n/5⌉为偶数的话,就找出其较大的中位数) 。然后以该元素作为划分基准,将所有的中位数进行划分。

将所有元素以基准为中心划为四个区域。此时所有处于左下区域的元素都小于基准。右上区域的元素都大于基准。即至少判断得到⌊n/10⌋*3个数字小于基准,⌊n/10⌋*3个数字大于基准。此时⌊n/10⌋*3>n/4 

经过该方法,每次递归至少可以省去1/4的元素。复杂度分析如下:

在此,我们的算法的递归调用只有在n>75时执行,当小于75时我们通过简单的排序然后输出第k大的数。

有⌊n/10⌋*3>n/4 若需要对左下区域的元素进行递归,则复杂度为为T(n/4)。否则需要T(3n/4)

最后,我们可以得到

关键代码:

//将x作为基准数将数组分割,返回x的位置
int Partition(int a[], int p, int r, int x)
{
    //i指向首元素的前一个位置,j指向尾元素的后一个位置
    int i = p - 1, j = r + 1;
    while (1)
    {
        //i从基准数右边的元素开始找,直到找到第一个大于等于基准数的元素
        while (a[++i] < x && i < r);
        //j从尾元素开始找,直到找到第一个小于等于基准数的元素
        while (a[--j] > x && j > p);
        //若i>=j,说明基准数的位置已找到,为j
        if (i >= j)
        {
            break;
        }
        //交换两个元素,使得基准数左边的数均不大于它,右边的数均不小于它
        Swap(a[i], a[j]);
    }
    //返回基准数的位置
    return j;
}


//找每组的中位数,返回中位数的位置i
int SearchMid(int a[], int p, int r)
{
    //建立与数组a同等大小的数组b
    int* b = new int[r - p + 1];
    //用数组b存放数组a(注意此时b的首地址为0,而a的首地址为p)
    for (int i = p; i <= r; ++i)
    {
        b[i - p] = a[i];
    }
    //将数组b排序,b[(r-p+1)/2]为中位数
    SelectSort(b, 0, r - p);
    for (int i = p; i <= r; ++i)
    {
        if (a[i] == b[(r - p + 1) / 2])
        {
            return i;
        }
    }
    delete[]b;
    return 0;
}


 
//线性划分
int Select(int a[], int p, int r, int k)
{
    if (r - p < 5)
    {
        SelectSort(a, p, r);
        return a[p + k - 1];
    }
    //分成n/5组,每组5个,找到每组的中位数并将它放到数组首元素的位置
    for (int i = 0; i <= (r - p - 4) / 5; ++i)
    {
        int mid = SearchMid(a, p + 5 * i, p + 5 * i + 4);
        Swap(a[mid], a[p + i]);
    }
    //找到各组中位数的中位数
    int x = Select(a, p, p + (r - p - 4) / 5, (r - p - 4) / 10 + 1);
    //按照中位数划分
    int i = Partition(a, p, r, x);
    //求较小数数组的长度
    len = i - p + 1;
    //若较小数数组的长度小于等于k,说明第k小的元素在这个数组内,将其递归
    if (k <= len)
    {
        return Select(a, p, i, k);
    }
    //否则,说明第k小的元素在较大数数组,将其递归
    else
    {
        return Select(a, i + 1, r, k - len);
    }
}

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值