本文来说下在海量数据下的topK问题

文章目录

  • 概述
  • 堆排序
    • 什么是堆
    • 堆排序的原理
  • 海量TopK问题
  • 本文小结

概述

排序算法是个老生常谈的问题,笔试要考,面试也问,不过翻来覆去也就那几个花样吧。大概理解一下各个算法的原理,记下表格里的数据,然后再试试手撕代码,基本上就没问题了。


从表格里可以看出,堆排序是一个时间和空间复杂度都比较优秀的算法,至于它的原理,看懂是肯定能轻易看懂的,但是我总觉得如果你不自己亲手写一遍,就很容易忘记。并且,用递归的话,代码也是很简短的,还没写过的同学,不妨自己试着敲一下吧。


堆排序

什么是堆

堆(heap)是一种数据结构,也被称为优先队列(priority queue)。队列中允许的操作是先进先出(FIFO),在队尾插入元素,在队头取出元素。而堆也是一样,在堆底插入元素,在堆顶取出元素,但是堆中元素的排列不是按照到来的先后顺序,而是按照一定的优先顺序排列的。这个优先顺序可以是元素的大小或者其他规则。

而二叉堆是一种特殊的堆,它是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。如下图。


堆排序的原理

堆排序(HeapSort)是指利用堆这种数据结构所设计的一种排序算法。它的关键在于建堆和调整堆。步骤主要如下:

  • 创建一个堆;
  • 把堆首(最大值)和堆尾互换;
  • 把堆的尺寸缩小1,并调整堆,把新的数组顶端数据调整到相应位置;
  • 重复步骤 2,直到堆的尺寸为1,此时排序结束。

当然,光看文字肯定不能很直观地理解,我们跟着图示来学习吧。现在,我们有一个待排序的数组 {2, 4, 3, 7, 5, 8},我们通过构建最大堆的方法来排序。


步骤说明如下

  1. 将待排序的数组视作完全二叉树,按层次遍历。
  2. 找到二叉树的最后一个非叶子节点,也就是最后一个节点的父节点。即是 (len-1)/2 索引在的位置。如果其子节点的值大于其本身的值,则把它和较大子节点进行交换,即将数字3和8交换。如果并没有子节点大于它,则无需交换。
  3. 循环遍历,继续处理前一个节点,由于此时 4<7 ,因此再次交换。
  4. 循环遍历,继续处理前一个节点,由于此时 2<8 ,因此再次交换。注意:如果某个节点和它的某个子节点交换后,该子节点又有子节点,系统还需要再次对该子节点进行判断,做相同处理。
  5. 遍历完成后得到一个最大堆。将每次堆排序得到的最大元素与当前规模的数组最后一个元素(假设下标为i)交换,然后再继续调整前 i - 1 的数组。遍历终止之后,得到一个自小到大的排序数组。

C++代码实现如下

void adjust(vector<int> &arr, int index, int len) {int left = 2 * index + 1;int right = 2 * index + 2;int max_index = index;if (left < len && arr[left] > arr[max_index]) max_index = left;if (right < len && arr[right] > arr[max_index]) max_index = right;if (max_index != index) {swap(arr[max_index], arr[index]);adjust(arr, max_index, len); // 继续调整子节点}
}
void heapSort(vector<int> &arr, int len) {// 将数组进行堆排序for (int i = len / 2 - 1; i >= 0; i--) {adjust(arr, i, len);}// 将每次堆排序得到的最大元素与当前规模的数组最后一个元素交换for (int i = len - 1; i >= 1; i--) {swap(arr[0], arr[i]);adjust(arr, 0, i);}
}

海量TopK问题

剑指Offer有这样一道题,求最小的K个数,题目描述:输入n个整数,找出其中最小的K个数。例如输入 4,5,1,6,2,7,3,8 这8个数字,则最小的4个数字是 1,2,3,4。

而在面试的时候,我们也可能遇到这样的问题:有一亿个浮点数,如何找出其中最大的10000个?

这类问题我们把称为TopK问题:指从大量数据(源数据)中获取最大(或最小)的K个数据。

最容易想到的方法当然是全部排序再进行查找,然而时间复杂度怎么也要O(nlog₂n),当n极其大时,该算法占用的内存也emmm。而我们题目所要求返回的只是前K个数据,所以没必要全部排序,做那么多无用功。我们可以先取下标 0~k-1 的局部数组,用它来维护一个大小为K的数组,然后遍历后续的数字,进行比较后决定是否替换。这时候堆排序就派上用场了。我们可以将前K个数字建立为一个最小(大)堆,如果是要取最大的K个数,则在后续遍历中,将数字与最小堆的堆顶数字进行比较,若比它大,则进行替换,然后再重新调整为最大堆。整个过程直至所有数字遍历完为止。时间复杂度为O(n*log₂K),空间复杂度为K。

C++代码实现如下

class Solution {public:void adjust(vector<int> &arr, int index, int len) {int left = 2 * index + 1;int right = 2 * index + 2;int max_index = index;if (left < len && arr[left] > arr[max_index]) max_index = left;if (right < len && arr[right] > arr[max_index]) max_index = right;if (max_index != index) {swap(arr[max_index], arr[index]);adjust(arr, max_index, len);}} void heapSort(vector<int> &arr, int len) {for (int i = len / 2 - 1; i >= 0; i--) {adjust(arr, i, len);}vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {if (k <= 0 || k > input.size()) {vector<int> nullVec;return nullVec;}// 因为要取最小的k个数,所以取前k个数字构建一个最大堆// 相反,如果是取最大的k个数,则构建一个最小堆vector<int> sortedArray(input.begin(), input.begin() + k);heapSort(sortedArray, k);// 将后面的数字与这个构建好的二叉堆进行比较 for (int i = k; i < input.size(); i++) {if (input[i] < sortedArray[0]) {sortedArray[0] = input[i];adjust(sortedArray, 0, k);}}for (int i = k - 1; i >= 1; i--) {swap(sortedArray[0], sortedArray[i]);adjust(sortedArray, 0, i);}return sortedArray;}
};

相似的TopK问题还有:

  1. 有10000000个记录,这些查询串的重复度比较高,如果除去重复后,不超过3000000个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。请统计最热门的10个查询串,要求使用的内存不能超过1GB。
  2. 有10个文件,每个文件1GB,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。按照query的频度排序。
  3. 有一个1GB大小的文件,里面的每一行是一个词,词的大小不超过16个字节,内存限制大小是1MB。返回频数最高的100个词。
  4. 提取某日访问网站次数最多的那个IP。
  5. 10亿个整数找出重复次数最多的100个整数。
  6. 搜索的输入信息是一个字符串,统计300万条输入信息中最热门的前10条,每次输入的一个字符串为不超过255B,内存使用只有1GB。
  7. 有1000万个身份证号以及他们对应的数据,身份证号可能重复,找出出现次数最多的身份证号。

对于这类问题,比如上面第1个,可以先利用hash表将查询串存储并计数,然后再构建最小堆,将查询串的个数进行比较从而得到结果。核心思想都是一样的。


本文小结

本文介绍了海量数据下的topK问题

海量数据的topK问题相关推荐

  1. 海量数据Top-k问题如何处理以及大数据查询如何优化

    一.海量数据的Top-K问题 方法一:分治思想/Hash映射 + HashMap统计 + 堆/快速/归并排序 第一步:针对数据太大,内存受限,只能是:把大文件取模映射成小文件:如果内存足够,直接has ...

  2. 面试常见海量数据场景题

    https://github.com/weitingyuk/LeetCode-Notes-Waiting/blob/main/2021-02-17/TopK.md https://segmentfau ...

  3. 8102秋季校招:我的校招经历+Android岗总结(面经+技巧)

    前言 这段时间,找工作也算是告一段落了,8102年(手动滑稽)对我真的是一个不平凡的一年,其中的辛酸只有自己知道,最近总算是有了些空闲时间,便总结下这个转折点–秋招的点点滴滴,也算是画上一个句号,同时 ...

  4. 最全BAT算法面试130题:阿里、百度、腾讯、京东、美团、今日头条

    [百度.阿里.腾讯.京东.美团.今日头条]等公司都会必考关于算法的面试题目,今天总结算法和题目如下: 算法基础: 第一:复杂度估算和排序算法(上) 1) 时间复杂度和空间复杂度 2)认识对数器 3)冒 ...

  5. 意料之外情理之中,就这样,我成为了一名程序媛

    这条路上的心路历程 2019,己亥猪年,深刻充实,但同时又恍恍惚惚,因为还没来得及认清即将离开校园的事实,2020年,便兵荒马乱的开始了,湖北武汉这场突如其来的疫情笼罩在这片本应该喜气祥和的大地上,最 ...

  6. 阿里长达57分钟的电话一面,全程无尿点!乔戈里实验室直系亲学弟的面经分享!...

    乔戈里实验室的学弟阿里面经,一面已过,当时和这位学弟聊起来的时候发现学弟做得是我当年坐的那个实验室位置,哈哈哈,挺有意思.这里和学弟要了一波面经,惊奇地发现学弟连答案都写上了,很用心了!(值得大家学习 ...

  7. 插播面试题:海量数据求最大值Topk或者是最小值Topk

    如果数据量堪称是海量的时候,我们还需要耗费大量的时间空间排序后在排序完成后取他们的前k个最大值或者是前k个最小值么?面对海量数据,并不要求所有的数据都排序成有序序列时,我们没有必要采用各式各样的排序算 ...

  8. 找出一个字符串中出现次数最多的字_海量数据中找出前k大数(topk问题)

    在海量数据中找出出现频率最好的前k个数,或者从海量数据中找出最大的前k个数,这类问题通常被称为top K问题. 针对top K类问题,通常比较好的方案是分治+Trie树/hash+小顶堆(就是上面提到 ...

  9. 海量数据相关面试问题(二):海量数据热点数据/出现频度/TOP-K问题(TOP-K 分而治之/Hash映射 / Hashmap统计频度 / 堆排序决出排名)

    文章目录 前引 海量数据相关面试问题(二):海量数据热点数据/出现频度/TOP-K问题(TOP-K Hash映射分而治之 / Hashmap统计频度 / 堆排序决出排名) 1.通用数据频度/热点数据 ...

最新文章

  1. 基于Vue的WebApp项目开发(四)
  2. ubuntu较快台湾源
  3. android 骨架屏刷新动画,ios - 原生骨架屏,网络加载过渡动画的封装
  4. linux开机自动启动(自启动)脚本、程序(初始化脚本)(海康摄像头自启动程序)
  5. RecSys 2016总结
  6. 三星1TB硬盘MM804RS不识别加电磁头打盘敲盘异响不识别开盘恢复数据
  7. UE4中的字符串转换
  8. 模型稳定后放在服务器上,把工程放在服务器上
  9. JQuery下锚点的平滑跳转
  10. 1015. vs项目编译成功INSTALL时
  11. 苹果A13和A11性能差距有多大?
  12. 安徽省2012年下半年计算机水平考试(二级 c语言程序设计),安徽省计算机等级级考试真题C语言2012年12月.doc...
  13. 第6章 vector向量容器
  14. bzoj 5297 [Cqoi2018]社交网络 高斯消元+Matrix-Tree定理
  15. 如何选择一款好的倾斜摄影相机
  16. office相关文件转pdf的几种方式
  17. RealityCapture摄影测量软件
  18. 《蜘蛛侠1,2,3》
  19. 三重积分(Triple Integral)
  20. MySQL8.0 之SQL(DQL)单表、多表查询(详细回顾篇)

热门文章

  1. 纷享逍客宣布完成E+轮融资 长山兴资本领投
  2. 电动葫芦使用注意事项(转载)
  3. 统计sql server数据库中所有表的记录数
  4. 【大型网站运维之道 天道 人道 运维之道】
  5. java数据可视化平台初步构想
  6. Algs4-2.3.8Quick.sort()在处理N个全部重复的元素时比较次数
  7. 专访uPlane陈宏强:手机遥控固定翼飞机还是蓝海
  8. 开发过程中的注意事项,想到哪写到哪
  9. java工程开发之图形化界面之(第六课)
  10. RHEL 7.0已发布 CentOS 7 即将到来