首页 > 编程语言 >快速选择算法

快速选择算法

时间:2023-06-13 15:34:53浏览次数:42  
标签:arr 元素 中位数 选择 算法 pivot 快速 rk

问题描述

给定一个长度为$n$的数组,如何在$O(n)$的时间复杂度内找到第$k$大的数。

思路

朴素的想法是先排序,然后直接找到第$k$个元素,时间复杂度为$O(n\log n)$。

我们可以利用快速排序的思想来解决这个问题,考虑快速排序的划分过程,在快速排序的“划分”结束后,数组$A_p \cdots A_r$被分成了$A_p\cdots A_q$和$A_{q+1}\cdots A_r$,此时可以按照左边元素的个数($q-p+1$)和$k$的大小关系来判断是只在左边还是右边递归的求解。

代码

template <Typename T> // 类型T需要定义 < 运算
// arr 为查找范围数组,rk 为需要查找的排名(从 0 开始),len 为数组长度
T find_kth_element(T arr[], int rk, const int len) {
    if (len <= 1) {
        return arr[0];
    }
    // 随机选择基准
    const T pivot = arr[rand() % len];
    // i 当前操作的元素
    // j 第一个等于pivot的元素
    // k 第一个大于pivot的元素
    // 完成一趟三路快排,将序列分为:
    // 小于 pivot 的元素 | 等于 pivot 的元素 | 大于 pivot 的元素
    int i = 0, j = 0, k = len;
    while (i < k) {
        if (arr[i] < pivot) {
            swap(arr[i++], arr[j++]);
        } else if (arr[i] > pivot) {
            swap(arr[i], arr[--k]);
        } else {
            ++i;
        }
    }
    // 根据要找的排名与两条分界线的位置,去不同的区间递归查找第k大的数
    // 如果小于pivot的元素个数比k多,则第k大的元素一定是一个小于pivot的数
    if (rk < j) {
        return find_kth_element(arr, rk, j);
    } else if (rk >= k){
        // 否则,如果小于pivot和等于pivot的元素加起来也没有k多
        // 则第k大的元素一定是一个大于pivot的元素
        return find_kth_element(arr + k, rk - k, len - k);
    } else {
        // 否则,pivot就是第k大的元素
        return pivot;
    }
}

优化:中位数的中位数

中位数中的中位数(英文:Median of medians),提供了一种确定性的选择划分过程中分界值的方法,从而能够让找第$k$大的数算法在最坏情况下也能实现线性时间复杂度。

该算法的流程如下:

  • 将整个序列划分为 $\left \lfloor \dfrac{n}{5} \right \rfloor$组,每组元素数不超过$5$个;
  • 寻找每组元素的中位数(因为元素个数较少,可以直接使用插入排序等算法);
  • 找出这$\left \lfloor \dfrac{n}{5} \right \rfloor$组元素中位数中的中位数。将该元素作为前述算法中每次划分时的分界值即可。

该优化后,最坏情况下,算法也有$O(n)$的时间复杂度。

参考

OI-Wiki:快速排序

标签:arr,元素,中位数,选择,算法,pivot,快速,rk
From: https://www.cnblogs.com/zwyyy456/p/17477655.html

相关文章

  • kmp算法
    问题描述kmp算法解决的是字符串匹配问题,即:字符串P是否是字符串S的子串?如果是,它出现在s的哪些位置?这里我们称S为主串,P为模式串。思路首先是暴力匹配算法(Brute-Force算法),代码如下:voidBruteForce(strings,stringp){intlen_s=s.size(),len_p=p.size();fo......
  • LRU 算法与 LFU 算法
    算法介绍LRULRU全称是LeastRecentlyUsed,即最近最久未使用算法。LRU根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高,它是页面置换算法的一种,也常用于缓存设计。LFULFU全称是LeastFrequentlyUsed,根据频率来选择要......
  • 代码随想录算法训练营第六天| 454.四数相加II 383. 赎金信 15. 三数之和 18. 四数
    454.四数相加II1,难点:1,多个数组之间,会有重复出现的数组,如果单用multiset也是会出错的2,如果用mutliset,在使用distance找出来equal_range的值的时候,也是会出现奇怪的错误的2,正确思路1,把重复出现的节点,次数存放到map种,然后进行遍历3,代码:1intfourSumCount(v......
  • 快速了解常用日志技术(JCL、Slf4j、JUL、Log4j、Logback、Log4j2)
    一、快速了解常用日志技术(JCL、Slf4j、JUL、Log4j、Logback、Log4j2)二、log4j2配置文件log4j2.xml文章目录一、简介二、日志门面三、SpringBoot使用Log4j2进行日志输出同步日志1、排除logback日志、导入log4j2依赖2、导入自定义log4j2.xml配置文件3.基础log4j2.xml配置文件异......
  • 选择http代理IP需要考虑哪些因素
    对于爬虫工作者来说,选择合适的代理IP是很重要的一项工作,正所谓“工欲善其事必先利其器”。那么选择http代理IP需要考虑哪些因素呢?1、价格很多人选择代理IP首先看重的就是价格,货比三家也只比价格,不可否认,价格确实很重要。毕竟公司是有预算限制的,但需要在质量相差不多的情况下,选择价......
  • 技术实战 —— 快速实现语聊房搭建
    语音相比文字图片更丰富,比视频又更简便,是天然的社交工具。以95后为代表的Z世代用户,在微信、QQ、微博等主流社交工具以外,更愿意尝试基于不同兴趣相对小众的社交工具。ZEGO即构科技推出语聊房解决方案,帮助客户快速搭建语聊房。本次分享,我们邀请到了即构科技交付解决方案专家JIN。......
  • vue时间选择器 nut-datepicker
    vue时间选择器https://blog.csdn.net/Marshall_Ma/article/details/1242444511、年-月-日时:分效果展示:打开选择器:<divclass="label">记录日期:</div><nut-cell:showIcon="true":isLink="true"@click.native="switchPicker"......
  • 如何快速做出产品MVP
    两个月前,我在生财有术分享了《如何获得产品idea》。下一步,就是把idea变成MVP。今天我们聊聊,如何快速做出MVP。一、为什么需要快速做出产品MVP?因为新产品的失败率太高了。为了提高整体成功率,我们只能增加数量。我们只能不断地尝试新的idea,这是所有创新者共同的宿命。在这样......
  • 湖边的烦恼-算法训练题
    湖边的烦恼-算法训练题-递归问题描述每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩。每天早上,租鞋窗口都会排起长龙,假设有还鞋的m个,有需要租鞋的n个。现在的问题是,这些人有多少种排法,可以避免出现体育......
  • 一张图快速了解 Istio 的 EnvoyFilter
    EnvoyFilter简介EnvoyFilter提供了一种机制来定制IstioPilot生成的Envoy配置。使用EnvoyFilter修改某些字段的值,添加特定的过滤器,甚至添加全新的侦听器、集群等等。这个功能必须谨慎使用,因为不正确的配置可能会破坏整个网格的稳定性。与其他Istio网络对象不同,EnvoyFil......