面试题 : Top-k问题

2023-11-15

目录

简介

题目

示例

提示

开始解题

1.思路

2.解题代码

3.时间复杂度

4.运行结果

​编辑

目前问题

真正的解法

1.以找前K个最大的元素为例

2.代码执行过程&&时间复杂度的计算

3.画图演示代码执行过程

4.解题代码

两种解法的比较

完结撒花✿ヽ(°▽°)ノ✿


59f82ab5b49840ff9d15ef16017f2e57.jpeg

 

博主推荐:毕竟面试题,还是动动你们的小手收藏一下,万一以后面试的时候遇到了,就赚到了!o(* ̄︶ ̄*)o

简介

对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决
1. 用数据集合中前K个元素来建堆

  • 前k个最大的元素,则建小堆
  • 前k个最小的元素,则建大堆

2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素

题目

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

示例

输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4
]

提示

  •   0 <= len(arr) <= 100000 
  •   0 <= k <= min(100000, len(arr))

开始解题

1.思路

  1. 建立小根堆,遍历这个数组把数组中的元素都放到小根堆中
  2. 定义一个数组ret作为返回值,,取k次堆顶元素放到数组中,返回ret

2.解题代码

public class Solution {
    /*
     * 这样写的话效率不是很高
     * */
    public int[] smallestK(int[] arr, int k) {
        int[] ret= new int[k];
        if(arr==null||k==0){
            return null;
        }
        //向上调整来建堆,时间复杂度为 O(N*logN)
        Queue<Integer> minHeap = new PriorityQueue<>();
        for (int i : arr) {
            minHeap.offer(i);
        }
        //poll() 移除优先级最高的元素并返回,如果优先级队列为空,返回null
        //每一次移除堆顶元素,都必须进行向下调整这棵二叉树,假设树的高度为h,时间复杂度log(h)
        //再加上循环k次,这段代码的时间复杂度为O(K * logH)
        for (int i = 0; i < k; i++) {
            ret[i]=minHeap.poll();
        }
        return ret;
    }
}

3.时间复杂度

        //向上调整来建堆,时间复杂度为 O(N*logN)
        Queue<Integer> minHeap = new PriorityQueue<>();
        for (int i : arr) {
            minHeap.offer(i);
        }
        //poll() 移除优先级最高的元素并返回,如果优先级队列为空,返回null
        //每一次移除堆顶元素,都必须进行向下调整这棵二叉树,假设树的高度为h,时间复杂度log(h)
        //再加上循环k次,这段代码的时间复杂度为O(K * logH)
        for (int i = 0; i < k; i++) {
            ret[i]=minHeap.poll();
        }

 所以上述解法的时间复杂度为:O(N*logN+K * logH)

4.运行结果

d633a78226354d24b2c6e05debd1996f.png

目前问题

代码运行效率不高,时间复杂度不行,太高了

       主要原因

        //向上调整来建堆,时间复杂度为 O(N*logN)
        Queue<Integer> minHeap = new PriorityQueue<>();
        for (int i : arr) {
            minHeap.offer(i);
        }
        //poll() 移除优先级最高的元素并返回,如果优先级队列为空,返回null
        //每一次移除堆顶元素,都必须进行向下调整这棵二叉树,假设树的高度为H,由节点总数与树的高度关系可得:N=2^H-1=>H=log(N+1)
        //再加上循环k次,这段代码的时间复杂度为O(K*logN)
        for (int i = 0; i < k; i++) {
            ret[i]=minHeap.poll();
        }


真正的解法

TOP-K问题:即求数据集合中前K个最大的元素或者最小的元素

TOP-K问题并不是将全部数据建立成堆,因为TOP-K问题一般情况下数据量都比较大。

真正的解法:是拿前K个建堆;找前K个最小的元素,建一个大根堆;找前K个最大的元素,建一个小根堆

TOP-K主要指的是在很大的一组数据的背景下进行,前K个元素仅仅只占很小的一部分,所以建堆和调整堆的时间复杂度也就变得很小了

1.以找前K个最大的元素为例

输入: arr = [27,15,19,18,28], k = 3

2.代码执行过程&&时间复杂度的计算

1.建立一个大小为K的小根堆(构造器默认的),没放元素,本质上是建立了一个大小为K的数组

2.遍历数组的前K个,放到小根堆minHeap当中 => 向上调整建堆
   时间复杂度: K*logK

3.遍历剩下的K-1个,每次和堆顶元素进行比较
   (1)如果该元素比堆顶元素小说明该元素一定不是前K个最大元素中的值,就不入堆;
   (2)如果该元素比堆顶元素大堆,则该元素与堆中最后个元素交换,再移除最后一个元素再把该元素入堆,
       入到最后一个先素的位置,调整该完全二叉树,使其再次成为个小根堆;
   时间复杂度:(N-K)*H=>(N-K)logK   注:(H为树的高度,K=2^H-1,H=log(K+1))
   (N-K)*H=>(N-K)*log(K+1)=>(N-K)logK

4.将堆中的元素放到ret里面,每次poll都是弹出堆中的最小值
   时间复杂度: K*logK

所以时间复杂度: K*logK+(N-K)*logK+ K*logK => N*logK + K*logK
   取近似值:O(N*logK)  注:K为常数,可忽略不计

3.画图演示代码执行过程

9c0d5e905ca74df7824c560c5e2f7338.png

 注:

  • 小根堆中是前K个最大的值
  • 堆顶元素是这K个最大的值里面最小的
  • 最后的堆顶元素就是第K大的元素(牢记,面试官可能会问到!!!)
  • 当遍历到数组元素大于堆顶的时候,说明此时堆顶的元素一定不是前K个最大的值

4.解题代码

    /*
    * 前k个最大的元素
    * 时间复杂度:K*logK
    * */
    public static int[] largestK(int[] arr, int k) {
        int[] ret = new int[k];
        if (arr==null||k==0){
            return null;
        }
        //1.建立一个大小为K的小根堆(构造器默认的),没放元素,本质上是建立了一个大小为K的数组

        Queue<Integer> minHeap = new PriorityQueue<>(k);
        //2.遍历数组的前K个,放到小根堆minHeap当中
        //时间复杂度: K*logK+(N-K)logK+ K*logK
        //取近似值:O(N*logK)
        for (int i = 0; i < k; i++) {
            minHeap.offer(arr[i]);
        }
      /* 3.遍历剩下的K-1个,每次和堆顶元素进行比较
        (1)如果该元素比堆顶元素小说明该元素一定不是前K个最大元素中的值,就不入堆;
        (2)如果该元素比堆顶元素大堆,则该元素与堆中最后个元素交换,再移除最后一个元素再把该元素入堆,
        入到最后一个先素的位置,调整该完全二叉树,使其再次成为个小根堆*/
        //时间复杂度:(N-K)*H=>(N-K)logK
        //注:(H为树的高度)K=2^H-1,H=log(K+1)
        //(N-K)*H=>(N-K)*log(K+1)=>(N-K)logK
        for (int i = k; i <arr.length ; i++) {
            int heapTop = minHeap.peek();
            if (arr[i]>heapTop){
                minHeap.poll();
                minHeap.offer(arr[i]);
            }
        }
        //4.将堆中的元素放到ret里面,每次poll都是弹出堆中的最小值
        //时间复杂度: K*logK
        for (int i = 0; i < k; i++) {
            ret[i]=minHeap.poll();
        }
        return ret;
    }

两种解法的比较

第一种解法时间复杂度为:O(N*logN+K * logN)

第二种解法时间复杂度为:O(N*logK)

注:K是常数,且数值与N相比极小

第二种解法远远优于第一种解法,面试官看到会给你竖起大拇指的

完结撒花✿✿ヽ(°▽°)ノ✿✿

b6170b68efed4ba9b22a6d9839c2c9ed.gif

 

 

 

 

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

面试题 : Top-k问题 的相关文章

随机推荐

  • 8k Byte , 8bit的ROM存储器,其地址线和数据线各需要多少根?

    总共容量为810248bits 2 16bits 因此其地址线需要16根 因为是8bit的ROM存储器 因此数据线需要8根
  • 使用ggplot2包在R语言中抑制数据轴上的科学计数法

    使用ggplot2包在R语言中抑制数据轴上的科学计数法 在数据可视化领域 ggplot2是R语言中最流行和强大的包之一 它提供了丰富的功能和灵活性 使我们能够创建出美观 清晰的图形来展示数据 其中一个常见的问题是 当我们使用ggplot2绘
  • 【自然语言处理】隐马尔可夫模型【Ⅵ】精度问题

    有任何的书写错误 排版错误 概念错误等 希望大家包含指正 由于字数限制 分成六篇博客 自然语言处理 隐马尔可夫模型 马尔可夫模型 自然语言处理 隐马尔可夫模型 隐马尔科夫模型概述 自然语言处理 隐马尔可夫模型 估计问题 自然语言处理 隐马尔
  • 非递减排列和非递增排列的定义

    递增排列 1 2 3 4 5 6 7 8 递减排列 8 7 6 5 4 3 2 1 非递减排列 1 2 3 4 5 6 6 7 8 8 非递增排列 9 8 8 7 6 5 2 2 1
  • 小白学python-数据清洗

    数据清洗 赔率 公路堵车模型的概念及应用 主成分分析PCA 新的的特征组合 车辆数据描述 one hot编码会使特征值大量增加 维度变高视情况而定 Logistic回归 AUC 曲线下的面积 求取素数以及赔率的代码 import opera
  • web service概念、架构及相关知识

    一 WebService的定义 WebService有好几种定义 W3C组织对其定义 WebService是一个软件系统 为了支持跨网络的机器间互操作交互而设计 WebService通常被定义为一组模块化的API 我们可以通过网络进行调用
  • 太原理工大学19年Java试题复习笔计

    19年Java复习题 1 为使一个名为Example的public类成功编译 需至少满足以下哪个条件 2 0分 A Example类中必须定义一个正确的main函数 B Example类中必须定义在 Example java源文件中 C E
  • sklearn 神经网络

    sklearn 神经网络 url https blog csdn net luanpeng825485697 article details 79064657 url sklearn 神经网络 多层感知器的优点 可以学习得到非线性模型 使用
  • 雷军发布会刚结束,就能写出上万字原创文章!

    前言 看完雷军演讲会之后你有没有看到过很多文章 成千上万个字的原创文章 瞬间就出现了 这是一个一个字敲的吗 当然不是 是AI 话不多说直接上教程 把雷军的演讲整理到笔记中 可以是md格式 word格式等等 复制粘贴即可 打开网站 smart
  • vmware workstation14连网

    记录一下手残的过程 1 选择NAT形式的连接 2 在桌面的右上角有个圆圈 右击这个图标 会显示一个有线连接 默认是关闭的 3 所以设置成连接状态 4 右击有线连接 进行网络配置 5 所有都配置成自动获取
  • MybatisPlus多表连接查询

    mybatis plus作为mybatis的增强工具 它的出现极大的简化了开发中的数据库操作 但是长久以来 它的联表查询能力一直被大家所诟病 一旦遇到left join或right join的左右连接 你还是得老老实实的打开xml文件 手写
  • mybatis与数据库连接过程

    菜鸟发文 请大神多多指导 1 准被一个maven项目 2 先导入jar包 3 配置mybatis核心文件 4 把连接数据库的配置项抽离出来 5 编写实体类 6 编写接口 7 编写mapper映射文件 8 把相同SQL session 方法抽
  • TCP三次握手-backlog队列问题

    TCP三次握手 backlog队列问题 md 概述 之前有同事做压力测试时 发现无论如何都无法突破128并发的问题 经排查发现该服务器ACCEPT QUEUE队列都为128 限制了网络的并发 TCP三次握手 Linux内核协议栈为一个TCP
  • 初识-常见浏览器兼容性问题与解决方案

    浏览器兼容问题一 不同浏览器的标签默认的外补丁和内补丁不同 问题症状 随便写几个标签 不加样式控制的情况下 各自的margin 和padding差异较大 碰到频率 100 解决方案 CSS里 margin 0 padding 0 备注 这个
  • 前后端利用accessToken与refreshToken无感刷新

    项目初衷 以jwt 由header payload和signature组成 为例 用户登录成功 后端返回accessToken 前端保存 请求接口携带 一切都是水到渠成的 可是在acessToken失效时 你正好请求一次接口 接口就挂了 可
  • SpringBoot集成ShedLock分布式定时任务

    文章目录 前言 一 背景 二 ShedLock是什么 三 落地实现 1 1 引入依赖包 1 2 配置数据库连接信息 1 3 创建Mysql数据表 1 4 配置LockProvider 1 5 创建定时Job 四 结果分析 前言 一 背景 在
  • 【性能测试】Jmeter —— jmeter计数器

    jmeter计数器 如果需要引用的数据量较大 且要求不能重复或者需要递增 那么可以使用计数器来实现 如 新增功能 要求名称不能重复 1 新增计数器 计数器 允许用户创建一个在线程组之内都可以被引用的计数器 计数器允许用户配置一个起点 一个最
  • 《Go语言在微服务中的崛起:为什么Go是下一个后端之星?》

    博主猫头虎 带您进入 Golang 语言的新世界 博客首页 猫头虎的博客 面试题大全专栏 文章图文并茂 生动形象 简单易学 欢迎大家来踩踩 IDEA开发秘籍专栏 学会IDEA常用操作 工作效率翻倍 100天精通Golang 基础入门篇 学会
  • c语言 常量表达式,Constant expressions(常量表达)

    几种表达式被称为常量表达式 预处理器常量表达式 if 或 elif 后面的表达式必须扩展为 除赋值 增量 减量 函数调用或逗号之外的其他操作符 其参数是预处理常量表达式 整数常量 字符常量 特殊的预处理器操作员 defined 当在 if表
  • 面试题 : Top-k问题

    目录 简介 题目 示例 提示 开始解题 1 思路 2 解题代码 3 时间复杂度 4 运行结果 编辑 目前问题 真正的解法 1 以找前K个最大的元素为例 2 代码执行过程 时间复杂度的计算 3 画图演示代码执行过程 4 解题代码 两种解法的比