Java 详细讲解用堆解决Top-k问题

yizhihongxing

Java 详细讲解用堆解决Top-k问题

问题描述

Top-k问题常常需解决业务中的热点,如商品销量排行、热搜关键词、热门文章等。假定要找出一个无序数组中前k大或前k小的元素,解决此问题有多种方法,下面我们主要介绍用堆排序算法解决Top-k问题。

思路及实现

1. 思路

用堆排序算法的思路如下:

  1. 建立一个大小为k的堆,如果堆里面元素数量未达到k,那么将当前元素加入堆中即可
  2. 如果当前元素大于堆顶元素,那么将堆顶元素替换成当前元素
  3. 遍历完整个数组后,堆中的元素就是前k大或前k小的元素

用大根堆求前k大的数,用小根堆求前k小的数。

2. 实现

下面是用Java实现Top-k问题的示例代码:

public class TopK {
    /**
     * 求数组中前k大的数
     *
     * @param nums 数组
     * @param k    k的大小
     * @return 前k大元素的List
     */
    public List<Integer> getTopK(int[] nums, int k) {
        PriorityQueue<Integer> minHeap = new PriorityQueue<>(k);

        for (int num : nums) {
            if (minHeap.size() < k) {
                minHeap.offer(num);
            } else if (num > minHeap.peek()) {
                minHeap.poll();
                minHeap.offer(num);
            }
        }

        return new ArrayList<>(minHeap);
    }

    /**
     * 求数组中前k小的数
     *
     * @param nums 数组
     * @param k    k的大小
     * @return 前k小元素的List
     */
    public List<Integer> getLowK(int[] nums, int k) {
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, Collections.reverseOrder());

        for (int num : nums) {
            if (maxHeap.size() < k) {
                maxHeap.offer(num);
            } else if (num < maxHeap.peek()) {
                maxHeap.poll();
                maxHeap.offer(num);
            }
        }

        return new ArrayList<>(maxHeap);
    }
}

上面代码中,我们用Java内置的优先队列PriorityQueue来实现堆排序,函数offer()用于插入堆中元素,函数poll()用于删除堆顶元素,而peek()则用于查看堆顶元素而不删除它。

接下来我们用一个数组和getTopK()函数来演示求前k大的数:

public static void main(String[] args) {
    int[] nums = {1, 3, 2, 5, 4, 8, 6, 0};
    int k = 4;

    TopK topK = new TopK();
    List<Integer> topKList = topK.getTopK(nums, k);

    System.out.println("前" + k + "大的的数为:" + topKList.toString());
}

控制台输出结果为:前4大的的数为:[3, 4, 5, 6],即数组中前4大的数是3, 4, 5, 6。

接着,我们来演示用getLowK()函数来求前k小的数:

public static void main(String[] args) {
    int[] nums = {1, 3, 2, 5, 4, 8, 6, 0};
    int k = 4;

    TopK topK = new TopK();
    List<Integer> lowKList = topK.getLowK(nums, k);

    System.out.println("前" + k + "小的的数为:" + lowKList.toString());
}

控制台输出结果为:前4小的的数为:[1, 0, 2, 3],即数组中前4小的数是1, 0, 2, 3。

以上示例代码和输出结果,均说明用堆排序算法可以很方便地解决Top-k问题。

小结

用堆排序算法解决Top-k问题,是一种时间复杂度为O(nlogk)的高效算法,它也是解决业务中Top-k问题的常用方法之一。

我们注意到PriorityQueue由于使用了大常数,堆排序的复杂度与快速排序相近:O(n * logn),但优于冒泡排序的O(n^2)。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 详细讲解用堆解决Top-k问题 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • jQuery密码强度验证控件使用详解

    jQuery密码强度验证控件使用详解 介绍 jQuery密码强度验证控件是一个用于实时检测密码强(安全)度的工具,它支持自定义安全等级,自定义强度条样式等。该控件简单易用,轻量级高效,易于开发者快速上手并集成到自己的项目中。 安装 jQuery密码强度验证控件可通过npm安装,命令如下: npm install jquery.password_strengt…

    other 2023年6月26日
    00
  • JavaScript中进制之间的转换

    JavaScript中进制之间的转换可以使用内置的方法和算法来实现。下面是一个完整的攻略,包括两个示例说明。 十进制转其他进制 十进制转二进制 使用toString()方法将十进制数转换为二进制字符串。 let decimalNumber = 10; let binaryNumber = decimalNumber.toString(2); console.…

    other 2023年5月5日
    00
  • Java中初始化List集合的八种方式汇总

    Java中初始化List集合的八种方式汇总 在Java中,List是一种非常常用的集合类型。那么如何在Java中初始化List集合呢?这篇文章将为大家详细讲解Java中初始化List集合的八种方式。 1. 使用ArrayList List<String> list1 = new ArrayList<>(); list1.add(&qu…

    other 2023年6月20日
    00
  • BigDecimal类

    BigDecimal类 在Java中,使用float或double类型来表示小数时,由于浮点数本质上是二进制的,因此在进行精确计算时可能会存在精度丢失的问题,这对于需要精确计算的场景来说是不能接受的。 为了解决这一问题,Java中提供了BigDecimal类,即可以精确表示数字的高精度类。本篇文章将分为以下几个部分介绍BigDecimal类的使用。 1. B…

    其他 2023年3月28日
    00
  • Asp.Net Core基础篇之:白话管道中间件

    Asp.Net Core基础篇之:白话管道中间件 在 Asp.Net Core 中,管道(Pipeline)是请求处理过程中的重要概念,是一组按顺序执行的中间件(Middleware)组成。本篇文章将详细讲解 Asp.Net Core 中的管道中间件。 什么是中间件? 在 Asp.Net Core 中,中间件是请求和响应模型的抽象。中间件是在管道中按顺序执行…

    其他 2023年3月28日
    00
  • 从汇编看c++的默认析构函数的使用详解

    下面就来详细讲解“从汇编看c++的默认析构函数的使用详解”的完整攻略。 一、C++的默认析构函数简介 在C++中,如果我们没有显式地为类定义析构函数,那么编译器会自动生成一个默认的析构函数,用于释放对象占用的内存。这样的析构函数不需要我们手动去写,像这样: class MyClass{ //… }; 如果在程序中我们创建了MyClass的对象,那么当这个…

    other 2023年6月26日
    00
  • 深入理解js函数的作用域与this指向

    深入理解JS函数的作用域与this指向攻略 1. 作用域(Scope)的概念 作用域是指在程序中定义变量的区域,它决定了变量的可见性和生命周期。在JavaScript中,作用域分为全局作用域和局部作用域。 全局作用域 全局作用域是指在整个程序中都可以访问的变量。在浏览器环境中,全局作用域通常是指在全局对象window下定义的变量。 示例1:全局作用域 var…

    other 2023年8月19日
    00
  • 解决vue2.0动态绑定图片src属性值初始化时报错的问题

    Vue 2.0中,对于动态绑定图片src属性时,初始化时可能会出现报错的问题。这个问题通常是由于绑定的图片地址为空字符串或者是undefined导致的,通过一些简单的方法,可以解决这个问题。接下来,我们就来详细讲解一下如何解决这个问题。 问题描述 在Vue 2.0中,我们经常会使用动态绑定的方式来绑定图片的src属性值,在初始化时就会将图片的url赋值给sr…

    other 2023年6月20日
    00
合作推广
合作推广
分享本页
返回顶部