基础算法——快速选择算法

问题的描述

快速选择

给定一个长度为 n n n的整数数列,以及一个整数 k k k,请用快速选择算法求出数列从小到大排序后的第 k k k个数。

输入输出数据要求

输入格式

第一行包含两个整数 n n n k k k
第二行包含 n n n个整数(所有整数均在 1 ∼ 109 1∼109 1109范围内),表示整数数列。

输出格式

输出一个整数,表示数列的第 k k k小的数。

数据范围

1 ≤ n ≤ 100000 1≤n≤100000 1n100000
1 ≤ k ≤ n 1≤k≤n 1kn

输入样例:

5 3
2 4 1 5 3

输出样例:

3

思路分析

总体思想

  1. 快速选择算法是建立在快速排序的基础上的,一种对快速排序算法的改进。快速选择算法依旧以分治为基本思想,与快速排序不同的是,快速选择是有条件的分治。
  2. 快速选择算法的主要思路如下:
    • 取分界点
    • 调整分界点两侧的区间使满足条件
    • 判断第k小的数字在分界点左侧还是在分界点右侧,并根据k所在的侧再进行以上步骤。
  3. 可以明显看出,只有第三步与快速排序不同。

具体步骤

  1. 选择数组的左端点的数值为分界点数值。
  2. 声明两个指针 i , j i,j i,j并令其分别指向数组左端点和右端点。
  3. 将指针 i , j i,j i,j向数组中间移动。先移动指针 i i i,如果指针 i i i所指向的数据小于分界点数值 x x x,那么继续移动i;否则停止移动指针 i i i,转为移动指针 j j j。如果指针 j j j所指向的数据大于分界点数值 x x x,那么继续移动指针 j j j;否则停止移动指针 j j j。将指针 i i i所指向的数据指针 j j j所指向的数据进行交换。从开头重复本步骤,直到 i , j i,j i,j相遇。
  4. 判断第 k k k小的数所在的位置,如果在以 x x x为分解点的左侧,那么对左侧重复以上步骤;否则在对右侧重复以上步骤。

功能代码

void quick_choose(int a[], int k, int l, int r){
//a[]快速选择的数组;k 第k小个数; l left快速选择区间的开始点;r right快速选择区间的结束点
    if(l >= r) return; //只有一个数据的数组不需要排序
    else{
        int x = a[l], i = l - 1, j = r + 1; //选择分界点x,声明左开始位点i,右开始位点j
        while(i < j){ //保持i和j不相遇的情况下
            do i ++; while(a[i] < x); //先移动i,a[i]小于x继续移动,否则移动j
            do j --; while(a[j] > x); //移动j,a[j]大于x继续移动,否则停止
            if(i < j) swap(a[i], a[j]); //a[i],a[j]互换位置
        } // 循环直到i和j相遇为支,此时i在j右侧,j在i右侧
        int sl = j - l + 1; //左侧元素的个数
        if(k <= sl) quick_choose(a, k, l, j);//如果第k小的数在左侧,那么对作左侧递归
        else quick_choose(a, k - sl, j + 1, r);//如果在右侧,那么对右侧递归,第k小变为第k-sl小
    }
}

代码

#include <iostream>
#include <algorithm>

using namespace std;

void quick_choose(int a[], int k, int l, int r){
    if(l >= r) return;
    else{
        int x = a[l], i = l - 1, j = r + 1;
        while(i < j){
            do i ++; while(a[i] < x);
            do j --; while(a[j] > x);
            if(i < j) swap(a[i], a[j]);
        }
        int sl = j - l + 1;
        if(k <= sl) quick_choose(a, k, l, j);
        else quick_choose(a, k - sl, j + 1, r);
    }
}

int main(){
    const int N = 1e5 + 10;
    int f[N];
    int n, k;
    
    cin >> n >> k;
    for(int i = 0; i < n; i ++){
        scanf("%d", &f[i]);
    }
    
    quick_choose(f, k, 0, n - 1);
    
    printf("%d", f[k - 1]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值