数据结构基础23:TopN问题

前言:如果实时展现热门文章,比如近8小时点击量最大的文章前100名。如果是你来开发这个功能,你怎么做?

1、TopN排行榜问题描述:

在系统中,我们经常会遇到这样的需求:将大量(比如几十万、甚至上百万)的对象进行排序,然后只需要取出最Top的前N名作为排行榜的数据,这即是一个TopN算法。常见的解决方案有三种:

(1)直接使用List的Sort方法进行处理。

(2)使用排序二叉树进行排序,然后取出前N名。

(3)使用最大堆排序,然后取出前N名。

 第一种方案的性能是最差的,后两种方案性能会好一些,但是还是不能满足我们的需求。最主要的原因在于使用二叉树和最大堆排序时,都是对所有的对象进行排序,而不是将代价花费在我们需要的少数的TopN上。对于堆结构来说,并不需要你获取所有的数据,只需要对前N个数据进行处理。因此可以通过堆的进入排出,用小顶堆实现,调整最小堆的时间复杂度为O(logN),总时间复杂度为O(nlogN)。

2、TopN算法思想

有大量数据n个,要求取出这组数据中最大的K个值。对于这个问题,解法有很多如排序,不过效率最高的要数最小堆。

  1. 取出数组的前n个元素,创建长度为n的最小堆。
  2. 从n开始循环数组的剩余元素,如果元素(a)比最小堆的根节点大,将a设置成最小堆的根节点,并让堆保持最小堆的特性。
  3. 循环完成后,最小堆中的所有元素就是需要找的最大的n个元素。

3、Java代码实现

1)首先是小根堆初始化的内部类

class MinHeap
{
    public int[] data; // 堆的存储结构 - 数组
    private int n;  // 数组heap的容量
	    
    // 将一个数组传入构造方法,并转换成一个小根堆
    public MinHeap(int[] data)
   {
	   this.data = data;
	   n = data.length;
	   buildHeap();
   }

   private void buildHeap() 
   {
    // 完全二叉树只有数组下标小于或等于 (data.length) / 2 - 1 的元素有孩子结点,遍历这些结点。
	for(int i = (n/2-1);i>=0;i--)
	    heapify(i);
   }
		
  private void swap(int[] nums,int i,int j)
  {
	 int temp = nums[i];
	 nums[i] = nums[j];
	 nums[j] = temp;
  }

  private void heapify(int parentIndex) 
  { // 自上往下堆化

	int temp = data[parentIndex];
	int childIndex = 2* parentIndex + 1;
	while (childIndex < n) 
	{
	        if(childIndex+1< n && data[childIndex+1] < data[childIndex])
	               childIndex ++;
	        if(temp<=data[childIndex])
	               break;
	        data[parentIndex] = data[childIndex];
	        parentIndex = childIndex;
	        childIndex = 2 * parentIndex + 1;
        }

	 data[parentIndex] = temp;
  }
}
	

2)TopN方法

 public class TonN
{

   class MinHeap{...};

   public static void main(String[] args)
   {
	        // 源数据
	        int[] data = {56,275,12,6,45,478,41,1236,456,12,546,45};
	 
	        // 获取Top5
	        int[] top5 = new TopN20().topK(data, 5);
	 
	        for(int i=0;i<5;i++)
	        {
	            System.out.println(top5[i]);
	        }
   }
 
   // 从data数组中获取最大的N个数
   private  int[] topK(int[] data,int k)
   {
	   // 先取K个元素放入一个数组topN中
	   int[] topN = new int[k];
	   for(int i = 0;i< k;i++)
	   {
	       topN[i] = data[i];
	   }
	 
	   // 转换成最小堆
	   MinHeap heap = new MinHeap(topN);
	 
	  // 从k开始,遍历data
	  for(int i= k;i<data.length;i++)
	   {
	       //如果第i个数字小于堆顶,则继续循环,否则将其替换为堆顶元素,然后进行向下调整
	       int root = data[0];
	 
	       // 当数据大于堆中最小的数(根节点)时,替换堆中的根节点,再转换成堆
	       if(data[i] > root)
	       {
	            heap.data[0] = data[i];
	            heap.heapify(0);
	       }
	   }
	 
       return topN;
   }


}

输出结果:

 

最后感谢前辈博客:

关于topN问题的几种解决方案

topN问题

深度解析某头条的一道TopN面试题!

面试必须掌握的十个海量数据问题及解决方案

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java架构何哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值