TopK问题详解-程序员宅基地

技术标签: topk  剑指offer  

题目描述

面试中经常会问到的一道题目:从n个未排序的数中得到的最大的k个数,称为TopK问题。(最小的k个数做法也相似)

基于partition函数

基于快速排序中的partition函数,时间复杂度为O(n),空间复杂度为O(1);需要改变输入;

(1)根据Partition函数得到索引值index,index前的数据均大于nums[index],index后的数据均小于nums[index]

(2)如果index = k-1,则已经划分完成;数组前k个数据即为最大的k个数

(3)如果index>k-1,begin=index+1;否则end = index-1

(4)重复(2)操作直到index = k-1

class SolutionI {
    
public:
    /*
     * 基于快排Partition函数
     * 时间复杂度O(n)空间复杂度O(1)需要改变输入
     */
    vector<int> getTopK(vector<int> nums, int k){
    
        if (nums.empty() || k > nums.size() || k<=0) return {
    };
        vector<int> ret;
        int begin = 0, end = nums.size()-1;
        int idx = Partition(nums,begin,end);
        while (idx != k-1){
    
            if (idx < k-1){
    
                begin = idx + 1;
                idx = Partition(nums,begin,end);
            }else{
    
                end = idx - 1;
                idx = Partition(nums,begin,end);
            }
        }

        for (int i = 0; i < k; ++i) {
    
            ret.push_back(nums[i]);
        }
        return ret;
    }

    // 返回索引值idx,idx前的元素均大于该处的元素值;idx后的元素均小于该处的元素值
    int Partition(vector<int> &nums, int begin, int end){
    
        if (begin > end) return begin;
        int key = nums[begin];    // 取最后一个值为基准值
        while (begin < end){
    
            while (nums[end] <= key && begin < end) --end;
            nums[begin] = nums[end];
            while (nums[begin] > key && begin < end) ++ begin;
            nums[end] = nums[begin];
        }
        nums[begin] = key;
        return begin;
    }
};

动态维护大小为k的堆,时间复杂度为O(nlogk),空间复杂度为O(k),无需改变输入;

multiset实现最小堆;其基于红黑树实现,查找、删除、插入时间复杂度为O(logk)

每次比较最小堆的堆顶元素(topK大元素中最小值)与当前元素,如果当前元素更大,替换该元素。

class SolutionII {
    
public:
    /*
     * 最小堆方法
     * 时间复杂度O(nlogk)空间复杂度O(k)
     */
    vector<int> getTopK(vector<int>& nums, int k){
    
        if (nums.empty() || k > nums.size() || k<1) return {
    };
        vector<int> ret;
        multiset<int, less<int>> m; // 最小堆,保存最大的K个数
        multiset<int, less<int>>::iterator it;
        for (int i = 0; i < nums.size(); ++i) {
    
            if(m.size()<k){
    
                m.insert(nums[i]);
            } else{
    
                it = m.begin();
                // 如果当前值大于topK的最小元素(最小堆堆顶),替换该值
                if (nums[i]> *it){
    
                    m.erase(it);
                    m.insert(nums[i]);
                }
            }
        }
        for (it = m.begin();it!=m.end();it++)
            ret.push_back(*it);

        return ret;
    }
};

两种算法的比较

基于Partition的解法 基于堆或红黑树
时间复杂度 O(n) O(nlogk)
是否需要修改输入数据
是否适用于海量数据

基于partition函数的平均时间复杂度更快,但是具有明显的限制,如会修改输入数组

基于堆或红黑树的方法,具有两个明显优点:(1)没有修改输入数据(2)适合海量数据的输入。由于内存大小有限,可能无法一次性将所有数据全部载入内存,这时可每次读入一个数字,再进行判断;只需要内存能够容纳k个数据即可,适用于n很大并且k很小的问题。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zjwreal/article/details/90674978

智能推荐

中国的铁路订票系统在世界上属于什么水平?-程序员宅基地

文章浏览阅读227次。????????关注后回复“进群”,拉你进程序员交流群????????来自公众号:沉默王二综合自知乎:https://www.zhihu.com/question/315887668大家好,今天分享的这篇:“中国的铁路订票系统在世界上属于什么水平?”我们来看看知友们都是如何评价我国铁路订票系统的——也就是大名鼎鼎的 12306。会非常有意思。先来看看这个 1.8 万赞的,我觉得说得非常有道理..._目前国内外对火车票订票系统设计研究水平

图的邻接矩阵和邻接表表示_udg 邻接表-程序员宅基地

文章浏览阅读701次。1.邻接矩阵用矩阵表示顶点与顶点间边的关系(是否有边)#define INFINITY INT_MAX#define MAX_VERTEX_NUM 20typdef enum{DG,DN,UDG,UDN}GraphKind;typedef struct{VertexType vexs[MAX_VERTEX_NUM];int arcs[MXA_VERTEX_NUM ] [MAX_VERTEX_NUM];int vexnum,arcnum;GraphKind kind;}MGraph;2_udg 邻接表

一步一步教你如何在AndroidStudio查看Android源码(AOSP源码)_如何用android studio 查看android源码-程序员宅基地

文章浏览阅读4.1w次,点赞10次,收藏56次。一步一步教你如何在AndroidStudio查看Android源码(AOSP源码)_如何用android studio 查看android源码

curl多线程下载类-程序员宅基地

文章浏览阅读250次。<?php /** * curl多线程下载类 */class MultiHttpRequest{ public $urls = array (); private $res = array (); private $curlopt_header = 0; private $method = "GET"; private $curlopt = array (); ..._curl 多线程下载

51单片机AD转换_单片机ad转换原理-程序员宅基地

文章浏览阅读1.9w次,点赞21次,收藏180次。51单片机AD转换电路设计实现关于AD转换的原理,大家在《数字电子技术》中已经学过,这里做过多的介绍,本文介绍一款经典的8位AD转换芯片ADC0804,基于51单片机设计AD转换电路,并完成测量值的转换。1 芯片引脚介绍CS:片选信号,低电平有效,即CS=0时候芯片才能正常工作,单独一个ADC0804芯片时候直接置零。当有多个芯片时候可以通过片选信号实现分时复用。WR:低电平有效,当WR信号由高到低时候实现一次ADC转换。RD:低电平有效,RD=0时候可以读取数据。Vin+:模拟电压输入端。_单片机ad转换原理

操作系统实验报告17:请求页面置换算法_页式存储管理及页面置换算法操作系统实验报告-程序员宅基地

文章浏览阅读1.7k次,点赞3次,收藏14次。操作系统实验报告17实验内容实验内容:虚拟存储管理。编写一个 C 程序模拟实现课件 Lecture24 中的请求页面置换算法包括FIFO、LRU (stack and matrix implementation)、Second chance,并设计输入用例验证结果。实验环境架构:Intel x86_64 (虚拟机)操作系统:Ubuntu 20.04汇编器:gas (GNU Assembler) in AT&T mode编译器:gcc技术日志实验内容原理页_页式存储管理及页面置换算法操作系统实验报告

随便推点

理财课堂日记第6天-程序员宅基地

文章浏览阅读600次。人生是场长跑,你是同学中的哪一个?要知道,高中时候,大家的世界观人生观基本上已经形成了,但是高中时候成绩好的,过了20年来看,未必是事业上最成功的我这里举四个人的例子:A男,我最好的朋友,大学毕业就进入设计院,作为他们系最好的学生被某建筑设计院看中。他当年没考上研究生竟然也是他的运气,因为和他同届考上研究生的人毕业后成为他的下属的下属。37岁当上设计院院长,成为该设计院最年轻院长..._晚上好同学,明晚07:45你的第一节儿理财课就开始了,我是你接下来九天课的专

C网络编程Socket中,Code Runner插件无法右键运行。_coderunner无法运行-程序员宅基地

文章浏览阅读193次。编写一个本地Web项目,如下。使用Ctrl + F5以及点击三角运行均没有问题,但右键Run Code报错。_coderunner无法运行

单元测试工具及资源推荐-程序员宅基地

文章浏览阅读56次。本文将简单介绍一下如下几种单元测试工具以及推荐一些学习资源。   1.NUnit  2.TestDriven.Net  3.NUnitForms  4.NUnitAsp  一.NUnit  提起大名鼎鼎的NUnit,我想没有几个不知道吧?NUnit是一个专门针对于.NET的单元测试框架。在这之前有针对Java的JUnit,针对 C++的 CPPUnit,它们都是属于xUni..._button tester

TEKTRONIX泰克DPO2002B混合信号示波器-程序员宅基地

文章浏览阅读37次。4.强大的解码能力:DPO2002B示波器提供了多种解码选件,支持常见的串行协议和总线通信解码,如I2C、SPI、UART等,方便工程师对通信数据进行分析和验证。3.灵活的触发功能:示波器支持多种触发方式,如边沿触发、脉冲宽度触发和序列触发等,根据需要灵活设置触发条件,捕获感兴趣的波形。1.高性能参数:该示波器具备200 MHz的带宽和1 GS/s的实时采样率,可捕获和显示高频信号的细节,确保准确的测量结果。

java/php/node.js/python基于web的网上订餐系统【2024年毕设】-程序员宅基地

文章浏览阅读832次,点赞21次,收藏18次。本系统带文档lw万字以上文末可领取本课题的JAVA源码参考。

华为云AppCube:体验快速搭建微信问卷小程序-程序员宅基地

文章浏览阅读876次,点赞17次,收藏20次。华为云AppCube:体验快速搭建微信问卷小程序_微信问卷小程序

推荐文章

热门文章

相关标签