【排序进阶】快速选择算法

一.定义

快速选择算法(Quickselect)是一种在未排序的数组中查找第k小/大元素的算法,时间复杂度为O(n)。它的基本思想是选择一个基准值(pivot),将数组分为两部分,一部分小于等于基准值,一部分大于基准值。然后根据k与基准值的大小关系,选择其中一部分进行递归搜索,直到找到第k小/大元素为止。

快速选择算法和快速排序算法的思路类似,但是快速选择算法只需要对一部分数组进行快速排序,而不需要对整个数组进行排序。因此,快速选择算法的平均时间复杂度为O(n),最坏时间复杂度为O(n^2),但是最坏情况出现的概率很小。

快速选择算法的时间复杂度为O(n),空间复杂度为O(1)

二.基本步骤 

  1. 选择一个基准值pivot,可以选择数组中的任意一个元素。

  2. 将数组分为两部分,一部分小于等于pivot,一部分大于pivot。

  3. 如果k小于等于左边部分的元素个数,那么继续在左边部分中查找第k小元素;否则,在右边部分中查找第k-left个小元素。

  4. 重复步骤2和3,直到找到第k小元素为止。

三.核心代码(注释讲解)  

void search(int left,int right) //递归函数,left和right表示当前搜索的区间
{
    int i=left,j=right,pivot=arr[(left+right)/2]; //取中间值作为基准值
    while(i<=j) //当i<=j时进行循环
    {
        while(arr[j]>pivot) //从右往左找到第一个小于等于基准值的数
            j--;
        while(arr[i]<pivot) //从左往右找到第一个大于等于基准值的数
            i++;
        if(i<=j) //如果i<=j,交换arr[i]和arr[j]的值
        {
            swap(arr[i],arr[j]);
            i++;
            j--;
        }
    }
    //快排后数组被划分为三块: left<=j<=i<=right
    if(k<=j) search(left,j); //如果k在左区间,只需要搜左区间
    else if(i<=k) search(i,right); //如果k在右区间,只需要搜右区间
    else //如果k在中间区间,直接输出arr[j+1]并结束程序
    {
        cout<<arr[j+1];
        exit(0); //任务完成,强制结束!
    }
}

四.具体题目 

P1923 【深基9.例4】求第 k 小的数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

五.【AC】答案  

#include<iostream>
#include<stdio.h>
using namespace std;

int arr[5000005],k; //定义全局变量,存储输入的数组和要查找的第k小数

inline long long read(){ //快速读入函数
    char cc=getchar();
    long long f=1,ans=0;
    while(cc<'0' ||cc>'9'){
        if(cc=='-')
            f=-1;
        cc=getchar();
    }
    while(cc>='0' && cc<='9'){
        ans=ans*10+ (cc-'0'); //注意这里是ans*10+...
        cc=getchar();
    }
    return f*ans;
}

void search(int left,int right) //递归函数,left和right表示当前搜索的区间
{
    int i=left,j=right,pivot=arr[(left+right)/2]; //取中间值作为基准值
    while(i<=j) //当i<=j时进行循环
    {
        while(arr[j]>pivot) //从右往左找到第一个小于等于基准值的数
            j--;
        while(arr[i]<pivot) //从左往右找到第一个大于等于基准值的数
            i++;
        if(i<=j) //如果i<=j,交换arr[i]和arr[j]的值
        {
            swap(arr[i],arr[j]);
            i++;
            j--;
        }
    }
    //快排后数组被划分为三块: left<=j<=i<=right
    if(k<=j) search(left,j); //如果k在左区间,只需要搜左区间
    else if(i<=k) search(i,right); //如果k在右区间,只需要搜右区间
    else //如果k在中间区间,直接输出arr[j+1]并结束程序
    {
        cout<<arr[j+1];
        exit(0); //任务完成,强制结束!
    }
}

int main()
{
    int n;
    n=read(); //读入数组长度
    k=read(); //读入要查找的第k小数
    for(int i=0;i<n;i++) //循环读入数组元素
        arr[i]=read();
    search(0,n-1); //从整个数组的区间开始搜索
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值