三路快排解决TopK问题

news2024/9/28 11:13:00

前言:

我们首先要明白什么是三路快排,什么是topk问题。

三路快排:

思想:

三路快排就是数组分3块,三个指针,先随机取一个基准值key,然后将数组划分为3个部分:

【小于key】【等于key】【大于key】

此时key的值的位置就确定了,然后再递归遍历小于key部分,和大于key的部分。

具体实现:根据nums[i]的值分类讨论

优化:用随机的方式选择基准元素

随机的实现就是先用srand函数种下一个种子,然后再调用rand函数。

原码:

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        //种下随机数种子
        srand(time(NULL));
        int left = 0;
        int right = nums.size()-1;
        qsort(nums,left,right);
        return nums;
    }

    //随机生成比较元素key
    int GetRandom(vector<int>& nums,int left,int right)
    {
        int r = rand();
        int key = nums[r%(right-left+1) + left];//最后结果需要加上left
        return key;
    }

    void qsort(vector<int>& nums,int l,int r)
    {
        if(l >= r) return;//判断条件写在第一行
        int key = GetRandom(nums,l,r);
        int i = l;
        int left = l - 1;
        int right = r + 1;
        //一趟遍历,将key的值固定顺序
        while(i < right)//注意循环遍历条件
        {
            if(nums[i] == key) i++;
            else if(nums[i] > key)
            {
                swap(nums[i],nums[--right]);
            }
            else
            {
                swap(nums[i++],nums[++left]);
            }
        }
        //递归调用
        qsort(nums,l,left);
        qsort(nums,right,r);
    }
};

第二个问题:什么是topk问题?

topk问题一般分为两大类:

第一大类就是找最大/最小的第k个元素,这一类只需要找一个元素即可。

第二大类就是最大/最小的k个元素,这一类是找一串数字。

在有上面的知识后,我们先解决第一类问题如何找第k个元素。

第一类问题:

215. 数组中的第K个最大元素

题目描述:

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

解法:

一般topk问题我们也是可以用堆排序区解决,但是这道题目的时间复杂度要求是O(N),而我们的堆排序的时间复杂度是N*LOGN,所以仍然是我们快速排序的主场!

我们的算法是建立在三路快排的思想上,我们根据已经将数组分为三部分的基础上,根据每一部分元素的数量与k进行比较来去确定具体在哪一个区间。

原码:

class Solution {
public:
//三路快排
    int findKthLargest(vector<int>& nums, int k) {
        int left = 0,right = nums.size()-1;
        //随机数种子
        srand(time(NULL));;
        int ans = qsort(nums,left,right,k);
        return ans;
    }

    int GetRandom(vector<int>& nums,int left,int right)
    {
        int r = rand();
        //注意加上left
        return nums[r%(right-left+1) + left];
    }

    int qsort(vector<int>& nums,int l,int r,int k)
    {
        //为什么不用考虑大于的情况,因为后续会判断
        if(l == r) return nums[l];
        int left = l - 1;
        int right = r + 1;
        int i = l;
        //先将数组分为三块
        int key = GetRandom(nums,l,r);
        //因为是第k大,所以排降序
        while(i < right)
        {
            if(nums[i] == key) i++;
            else if(nums[i] < key) swap(nums[i++],nums[++left]);
            else swap(nums[i],nums[--right]); 
        }
        //分情况讨论,第k大元素具体在哪一段,直接return
        int c = r - right + 1,b = right - left - 1;
        if(c >= k) return qsort(nums,right,r,k);
        else if(b + c >= k) return key;
        else return qsort(nums,l,left,k-b-c);
    }
};

再解决第二类问题:

面试题 17.14. 最小K个数

题目描述:

设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。

解法:

这一题跟上一题寻找第K个元素,思想基本一样,都是将寻找的区间缩小,本题返回值是一串数字,直接返回{nums.begin(), nums.begin()+k}即可

原码:

class Solution {
public:
    //三路快排
    vector<int> smallestK(vector<int>& arr, int k) {
        srand(time(NULL));
        int left = 0,right = arr.size()-1;
        qsort(arr,left,right,k);
        //注意返回值的书写方式
        return {arr.begin(),arr.begin()+k};
    }

    int getRandom(vector<int>& arr,int left,int right)
    {
        int r = rand();
        return arr[r%(right-left+1) + left];
    }

    void qsort(vector<int>& arr,int l,int r,int k)
    {
        if(l >= r) return;
        int left = l - 1,right = r + 1;
        int key = getRandom(arr,l,r);
        int i = l;
        while(i < right)//不能等于!
        {
            if(arr[i] == key) i++;
            else if(arr[i] > key) swap(arr[i],arr[--right]);
            else swap(arr[i++],arr[++left]);
        }
        //分情况讨论
        //[l,left] [left+1,right-1] [right,r]
        int a = left - l + 1;
        int b = right - left - 1;
        if(k <= a) qsort(arr,l,left,k);
        else if(k <= a+b) return;
        else qsort(arr,right,r,k-a-b);
    }

};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1429384.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

客户端和服务端的简介

Client 和 Server 客户端&#xff08;Client&#xff09; 或称用户端&#xff0c;是指与服务器相对应&#xff0c;为客户提供本地服务的程序。除了一些只在本地运行的应用程序之外&#xff0c;一般安装在客户机上&#xff0c;需要与服务端互相配合运行。例如&#xff1a;下载 Q…

决策树的相关知识点

&#x1f4d5;参考&#xff1a;ysu老师课件西瓜书 1.决策树的基本概念 【决策树】&#xff1a;决策树是一种描述对样本数据进行分类的树形结构模型&#xff0c;由节点和有向边组成。其中每个内部节点表示一个属性上的判断&#xff0c;每个分支代表一个判断结果的输出&#xff…

爬虫笔记(三):实战qq登录

咳咳&#xff0c;再这样下去会进橘子叭hhhhhh 以及&#xff0c;这个我觉得大概率是成功的&#xff0c;因为测试了太多次&#xff0c;登录并且验证之后&#xff0c;qq提醒我要我修改密码才可以登录捏QAQ 1. selenium 有关selenium具体是啥&#xff0c;这里就不再赘述了&#x…

用C++实现一个哈希桶并封装实现 unordered_map 和 unordered_set

目录 哈希桶的实现 封装 unordered_map 和 unordered_set 封装代码 HashTable.h MyUnorderedMap.h MyUnorderedSet.h 哈希桶&#xff0c;又叫开散列法。开散列法又叫链地址法(开链法)&#xff0c;首先对关键码集合用散列函数计算散列地址&#xff0c;具有相同地址的关键码…

spring问题点

1.事务 1.1.事务传播 同一个类中 事务A调非事务B B抛异常 AB事务生效&#xff08;具有传播性&#xff09; 同一个类中 事务A调非事务B A抛异常 AB事务生效 也就是主方法加了事务注解 则方法内调用的其他本类方法无需加事务注解&#xff0c; 发生异常时可以保证事务的回滚 最常…

安科瑞消防设备电源监控系统在地铁工程的设计与应用

【摘要】&#xff1a;本文介绍了地铁工程中消防设备电源监控系统设置的必要性及规范求&#xff0c;分析了监控设计方案&#xff0c;提出该系统在地铁工程中的应用要求及建议&#xff0c;以供地铁工程建设参考。消防设备电源监控系统主要针对消防用电设备的电源进行实时的监控&a…

在 Elastic Agent 中为 Logstash 输出配置 SSL/TLS

要将数据从 Elastic Agent 安全地发送到 Logstash&#xff0c;你需要配置传输层安全性 (TLS)。 使用 TLS 可确保你的 Elastic Agent 将加密数据发送到受信任的 Logstash 服务器&#xff0c;并且你的 Logstash 服务器从受信任的 Elastic Agent 客户端接收数据。 先决条件 确保你…

深入理解指针(3)

⽬录 1. 字符指针变量 2. 数组指针变量 3. ⼆维数组传参的本质 4. 函数指针变量 5. 函数指针数组 6. 转移表 1. 字符指针变量 在指针的类型中我们知道有⼀种指针类型为字符指针 char* ; ⼀般使⽤: int main() {char ch w;char *pc &ch;*pc w;return 0; } 还有…

面试150 二进制求和 位运算

Problem: 67. 二进制求和 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 参考 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) Code class Solution {public String addBinary(String a, String b){StringBuilder ans new Stri…

某赛通电子文档安全管理系统 UploadFileToCatalog SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

【类和对象】4

日期类的拓展 c语言中的printf函数只能打印内置类型&#xff0c;为了弥补这一不足&#xff0c;c利用运算符重载可以打印自定义类型。 void operator<<(ostream&out);//声明在date.h中void Date::operator<<(ostream& out)//定义在date.cpp中 {out<<…

C++学习Day01之namespace命名空间

目录 一、程序及输出1.1 命名空间用途&#xff1a; 解决名称冲突1.2 命名空间内容1.3 命名空间必须要声明在全局作用域下1.4 命名空间可以嵌套命名空间1.5 命名空间开放&#xff0c;可以随时给命名空间添加新的成员1.6 命名空间可以是匿名的1.7 命名空间可以起别名 二、分析与总…

常见关系型数据库产品介绍

更新晚了&#xff0c;不好意思啦&#xff01;继关系型数据库的介绍与历史今天主要和大家分享关系型数据库有哪些产品以及简单的背景介绍。这篇文章介意宝宝们听着舒缓的音乐静静享受。 关系型数据库的产品有很多&#xff0c;下面和大家分享一些比较有名的、使用比较广泛的关系…

了解野指针与assert断言 拿捏指针的使用!

目录 1.野指针 野指针的成因&#xff1a; 2.规避野指针 3.assert断言 创作不易&#xff0c;宝子们&#xff01;如果这篇文章对你们有帮助的话&#xff0c;别忘了给个免费的赞哟~ 1.野指针 概念&#xff1a;野指针就是指针指向的位置是不可知的&#xff08;随机的、不正确的…

AspNet web api 和mvc 过滤器差异

最近在维护老项目。定义个拦截器记录接口日志。但是发现不生效 最后发现因为继承的 ApiController不是Controller 只能用 System.Web.Http下的拦截器生效。所以现在总结归纳一下 Web Api: System.Web.Http.Filters.ActionFilterAttribute 继承该类 Mvc: System.Web.Mvc.Ac…

《苍穹外卖》电商实战项目实操笔记系列(P123~P184)【下】

史上最完整的《苍穹外卖》项目实操笔记系列【下篇】&#xff0c;跟视频的每一P对应&#xff0c;全系列10万字&#xff0c;涵盖详细步骤与问题的解决方案。如果你操作到某一步卡壳&#xff0c;参考这篇&#xff0c;相信会带给你极大启发。 上篇&#xff1a;P1~P65《苍穹外卖》项…

【Python机器学习系列】建立多层感知机模型预测心脏疾病(完整实现过程)

一、引言 前文回顾&#xff1a; 【Python机器学习系列】建立决策树模型预测心脏疾病&#xff08;完整实现过程&#xff09; 【Python机器学习系列】建立支持向量机模型预测心脏疾病&#xff08;完整实现过程&#xff09; 【Python机器学习系列】建立逻辑回归模型预测心脏疾…

Java 基于 SpringBoot+Vue 的考研论坛管理系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Transformer实战-系列教程2:Transformer算法解读2

&#x1f6a9;&#x1f6a9;&#x1f6a9;Transformer实战-系列教程总目录 有任何问题欢迎在下面留言 Transformer实战-系列教程1&#xff1a;Transformer算法解读1 Transformer实战-系列教程2&#xff1a;Transformer算法解读2 5、Multi-head机制 在4中我们的输入是X&#x…

【内置对象·js】

数学对象 document.write("圆周率为 " Math.PI "<br>");日期对象 var date new Date(); // 实例化 Date 对象var month date.getMonth() 1; // 获取月份&#xff0c;取值为 0&#xff08;一月&#xff09;到 11&#xff08;十二月&#xff09;之…