René's URL Explorer Experiment


Title: 前端进阶算法10:别再说你不懂topk问题了 · Issue #73 · sisterAn/JavaScript-Algorithms · GitHub

Open Graph Title: 前端进阶算法10:别再说你不懂topk问题了 · Issue #73 · sisterAn/JavaScript-Algorithms

X Title: 前端进阶算法10:别再说你不懂topk问题了 · Issue #73 · sisterAn/JavaScript-Algorithms

Description: 引言 今天这篇文章讲解一道活跃于各个大厂(腾讯、字节、阿里等)面试中的题目:Top K 问题,将按照以下脉络介绍: 什么是 Top K 问题 Top K 问题的五种经典解法 快速回顾与总结 练习题 Top K 三剑客:最小 K 个数、前 K 个高频元素、第 K 个最小元素(含解答) 下面直接开始吧👇 一、什么是 Top K 问题 什么是 Top K 问题?简单来说就是在一组数据里面找到频率出现最高的前 K 个数,或前 K 大(当然也可以是前 K 小)的数。 经典的 To...

Open Graph Description: 引言 今天这篇文章讲解一道活跃于各个大厂(腾讯、字节、阿里等)面试中的题目:Top K 问题,将按照以下脉络介绍: 什么是 Top K 问题 Top K 问题的五种经典解法 快速回顾与总结 练习题 Top K 三剑客:最小 K 个数、前 K 个高频元素、第 K 个最小元素(含解答) 下面直接开始吧👇 一、什么是 Top K 问题 什么是 Top K 问题?简单来说就是在一组数据里面找到频率出...

X Description: 引言 今天这篇文章讲解一道活跃于各个大厂(腾讯、字节、阿里等)面试中的题目:Top K 问题,将按照以下脉络介绍: 什么是 Top K 问题 Top K 问题的五种经典解法 快速回顾与总结 练习题 Top K 三剑客:最小 K 个数、前 K 个高频元素、第 K 个最小元素(含解答) 下面直接开始吧👇 一、什么是 Top K 问题 什么是 Top K 问题?简单来说就是在一组数据里面找到频率出...

Opengraph URL: https://github.com/sisterAn/JavaScript-Algorithms/issues/73

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"前端进阶算法10:别再说你不懂topk问题了","articleBody":"![](http://resource.muyiy.cn/image/20200629004211.jpg)\r\n\r\n### 引言\r\n\r\n今天这篇文章讲解一道活跃于各个大厂(腾讯、字节、阿里等)面试中的题目:Top K 问题,将按照以下脉络介绍:\r\n\r\n- 什么是 Top K 问题\r\n- Top K 问题的五种经典解法\r\n- 快速回顾与总结\r\n- 练习题 Top K 三剑客:最小 K 个数、前 K 个高频元素、第 K 个最小元素(含解答)\r\n\r\n下面直接开始吧👇\r\n\r\n### 一、什么是 Top K 问题\r\n\r\n\u003e 什么是 Top K 问题?简单来说就是在一组数据里面找到频率出现最高的前 K 个数,或前 K 大(当然也可以是前 K 小)的数。\r\n\r\n经典的 Top K 问题有:最大(小) K 个数、前 K 个高频元素、第 K 个最大(小)元素\r\n\r\n下面以求数组中的第 K 个最大元素为例,介绍 Top K 问题的解法\r\n\r\n**题目:**\r\n\r\n在未排序的数组中找到第 **k** 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素。\r\n\r\n**示例:**\r\n\r\n```js\r\n输入: [4,5,1,6,2,7,3,8] 和 k = 4\r\n输出: 5\r\n```\r\n\r\n### 二、解法:全局排序,取第 k 个数\r\n\r\n我们能想到的最简单的就是将数组进行排序(可以是最简单的快排),取前 K 个数就可以了,so easy\r\n\r\n**代码实现:**\r\n\r\n```js\r\nlet findKthLargest = function(nums, k) {\r\n    nums.sort((a, b) =\u003e b - a).slice(0, k);\r\n    return nums[k-1]\r\n};\r\n```\r\n\r\n**复杂度分析:**\r\n\r\n- 时间复杂度:O(nlogn)\r\n- 空间复杂度:O(logn)\r\n\r\n**注意:** \r\n\r\n在 V8 引擎 7.0 版本之前,数组长度小于10时, `Array.prototype.sort()` 使用的是插入排序,否则用快速排序。\r\n\r\n在 V8 引擎 7.0 版本之后就舍弃了快速排序,因为它不是稳定的排序算法,在最坏情况下,时间复杂度会降级到 O(n\u003csup\u003e2\u003c/sup\u003e)。 \r\n\r\n而是采用了一种混合排序的算法:**TimSort** 。 \r\n\r\n这种功能算法最初用于Python语言中,严格地说它不属于以上10种排序算法中的任何一种,属于一种混合排序算法: \r\n\r\n在数据量小的子数组中使用**插入排序**,然后再使用**归并排序**将有序的子数组进行合并排序,时间复杂度为 `O(nlogn)` 。\r\n\r\n### 三、解法:局部排序,冒泡\r\n\r\n题目仅仅需要求出数组中的第K个最大元素,没必要对数组整体进行排序\r\n\r\n可以使用冒泡排序,每次将最大的数在最右边冒泡出来,只冒泡 k次即可\r\n\r\n**动图演示**\r\n\r\n![](http://resource.muyiy.cn/image/20200628235530.gif)\r\n\r\n\r\n**代码实现**\r\n\r\n```js\r\nlet findKthLargest = function(nums, k) {\r\n    // 进行k轮冒泡排序\r\n    bubbleSort(nums, k)\r\n    return nums[nums.length-k]\r\n}\r\n\r\nlet bubbleSort = function(arr, k) {\r\n    for (let i = 0; i \u003c k; i++) {\r\n        // 提前退出冒泡循环的标识位\r\n        let flag = false;\r\n        for (let j = 0; j \u003c arr.length - i - 1; j++) {\r\n            if (arr[j] \u003e arr[j + 1]) {\r\n                const temp = arr[j];\r\n                arr[j] = arr[j + 1];\r\n                arr[j + 1] = temp;\r\n                flag = true;\r\n                // 表示发生了数据交换\r\n            }\r\n        }\r\n        // 没有数据交换\r\n        if(!flag) break\r\n    }\r\n}\r\n```\r\n\r\n**复杂度分析:**\r\n\r\n- 时间复杂度:最好时间复杂度 O(n),平均时间复杂度 O(n*k)\r\n- 空间复杂度:O(1)\r\n\r\n### 四、解法:构造前 `k` 个最大元素小顶堆,取堆顶\r\n\r\n我们也可以通过构造一个前 `k` 个最大元素小顶堆来解决,小顶堆上的任意节点值都必须小于等于其左右子节点值,即堆顶是最小值。\r\n\r\n所以我们可以从数组中取出 `k` 个元素构造一个小顶堆,然后将其余元素与小顶堆对比,如果大于堆顶则替换堆顶,然后堆化,所有元素遍历完成后,堆中的堆顶即为第 `k` 个最大值\r\n\r\n具体步骤如下:\r\n\r\n- 从数组中取前 `k` 个数( `0` 到 `k-1` 位),构造一个小顶堆\r\n- 从 `k` 位开始遍历数组,每一个数据都和小顶堆的堆顶元素进行比较,如果小于堆顶元素,则不做任何处理,继续遍历下一元素;如果大于堆顶元素,则将这个元素替换掉堆顶元素,然后再堆化成一个小顶堆。\r\n- 遍历完成后,堆顶的数据就是第 K 大的数据\r\n\r\n![](http://resource.muyiy.cn/image/20200628231850.png)\r\n\r\n![](http://resource.muyiy.cn/image/20200628231927.png)\r\n\r\n**代码实现:**\r\n\r\n```js\r\nlet findKthLargest = function(nums, k) {\r\n    // 从 nums 中取出前 k 个数,构建一个小顶堆\r\n    let heap = [,], i = 0\r\n    while(i \u003c k) {\r\n       heap.push(nums[i++]) \r\n    }\r\n    buildHeap(heap, k)\r\n    \r\n    // 从 k 位开始遍历数组\r\n    for(let i = k; i \u003c nums.length; i++) {\r\n        if(heap[1] \u003c nums[i]) {\r\n            // 替换并堆化\r\n            heap[1] = nums[i]\r\n            heapify(heap, k, 1)\r\n        }\r\n    }\r\n    \r\n    // 返回堆顶元素\r\n    return heap[1]\r\n};\r\n\r\n// 原地建堆,从后往前,自上而下式建小顶堆\r\nlet buildHeap = (arr, k) =\u003e {\r\n    if(k === 1) return\r\n    // 从最后一个非叶子节点开始,自上而下式堆化\r\n    for(let i = Math.floor(k/2); i\u003e=1 ; i--) {\r\n        heapify(arr, k, i)\r\n    }\r\n}\r\n\r\n// 堆化\r\nlet heapify = (arr, k, i) =\u003e {\r\n    // 自上而下式堆化\r\n    while(true) {\r\n        let minIndex = i\r\n        if(2*i \u003c= k \u0026\u0026 arr[2*i] \u003c arr[i]) {\r\n            minIndex = 2*i\r\n        }\r\n        if(2*i+1 \u003c= k \u0026\u0026 arr[2*i+1] \u003c arr[minIndex]) {\r\n            minIndex = 2*i+1\r\n        }\r\n        if(minIndex !== i) {\r\n            swap(arr, i, minIndex)\r\n            i = minIndex\r\n        } else {\r\n            break\r\n        }\r\n    }\r\n}\r\n\r\n// 交换\r\nlet swap = (arr, i , j) =\u003e {\r\n    let temp = arr[i]\r\n    arr[i] = arr[j]\r\n    arr[j] = temp\r\n}\r\n```\r\n\r\n**复杂度分析:**\r\n\r\n- 时间复杂度:遍历数组需要 O(n) 的时间复杂度,一次堆化需要 O(logk) 时间复杂度,所以利用堆求 Top k 问题的时间复杂度为 O(nlogk)\r\n- 空间复杂度:O(k)\r\n\r\n#### 利用堆求 Top k 问题的优势\r\n\r\n这种求 Top k 问题是可以使用排序来处理,但当我们需要在一个动态数组中求 Top k 元素怎么办喃?\r\n\r\n动态数组可能会插入或删除元素,难道我们每次求 Top k 问题的时候都需要对数组进行重新排序吗?那每次的时间复杂度都为 O(nlogn)\r\n\r\n这里就可以使用堆,我们可以维护一个 K 大小的小顶堆,当有数据被添加到数组中时,就将它与堆顶元素比较,如果比堆顶元素大,则将这个元素替换掉堆顶元素,然后再堆化成一个小顶堆;如果比堆顶元素小,则不做处理。这样,每次求 Top k 问题的时间复杂度仅为 O(logk)\r\n\r\n更多堆内容可查看 [前端进阶算法9:看完这篇,再也不怕堆排序、Top K、中位数问题面试了](https://github.com/sisterAn/JavaScript-Algorithms/issues/60)\r\n\r\n### 五、解法:优化,快速选择(quickselect)算法\r\n\r\n无论是排序算法还是构造堆求解 Top k问题,我们都经过的一定量的不必要操作:\r\n\r\n- 如果使用排序算法,我们仅仅想要的是第 k 个最大值,但是我们却对数组进行了整体或局部的排序\r\n- 如果使用堆排序,需要维护一个大小为 `k` 的堆(大顶堆,小顶堆),同时花费时间也很昂贵,时间复杂度为 `O(nlogk)`\r\n\r\n那么有没有一种算法,不需要进行排序,也不需要花费额外的空间,就能获取第 K 个最大元素喃?\r\n\r\n这就要说到快速选择算法了😊\r\n\r\n快速选择(quickselect)算法与快排思路上相似,下面普及一下快排,已经了解的朋友可以跳过这一段\r\n\r\n#### 快排\r\n\r\n快排使用了分治策略的思想,所谓分治,顾名思义,就是分而治之,将一个复杂的问题,分成两个或多个相似的子问题,在把子问题分成更小的子问题,直到更小的子问题可以简单求解,求解子问题,则原问题的解则为子问题解的合并。\r\n\r\n快排的过程简单的说只有三步:\r\n\r\n- 首先从序列中选取一个数作为基准数\r\n- 将比这个数大的数全部放到它的右边,把小于或者等于它的数全部放到它的左边 (一次快排 `partition`)\r\n- 然后分别对基准的左右两边重复以上的操作,直到数组完全排序\r\n\r\n具体按以下步骤实现:\r\n\r\n- 1,创建两个指针分别指向数组的最左端以及最右端\r\n- 2,在数组中任意取出一个元素作为基准\r\n- 3,左指针开始向右移动,遇到比基准大的停止\r\n- 4,右指针开始向左移动,遇到比基准小的元素停止,交换左右指针所指向的元素\r\n- 5,重复3,4,直到左指针超过右指针,此时,比基准小的值就都会放在基准的左边,比基准大的值会出现在基准的右边\r\n- 6,然后分别对基准的左右两边重复以上的操作,直到数组完全排序\r\n\r\n注意这里的基准该如何选择喃?最简单的一种做法是每次都是选择最左边的元素作为基准:\r\n\r\n![](http://resource.muyiy.cn/image/20200627224355.gif)\r\n\r\n但这对几乎已经有序的序列来说,并不是最好的选择,它将会导致算法的最坏表现。还有一种做法,就是选择中间的数或通过 `Math.random()` 来随机选取一个数作为基准,下面的代码实现就是以随机数作为基准。\r\n\r\n**代码实现**\r\n\r\n```js\r\nlet quickSort = (arr) =\u003e {\r\n  quick(arr, 0 , arr.length - 1)\r\n}\r\n\r\nlet quick = (arr, left, right) =\u003e {\r\n  let index\r\n  if(left \u003c right) {\r\n    // 划分数组\r\n    index = partition(arr, left, right)\r\n    if(left \u003c index - 1) {\r\n      quick(arr, left, index - 1)\r\n    }\r\n    if(index \u003c right) {\r\n      quick(arr, index, right)\r\n    }\r\n  }\r\n}\r\n\r\n// 一次快排\r\nlet partition = (arr, left, right) =\u003e {\r\n  // 取中间项为基准\r\n  var datum = arr[Math.floor(Math.random() * (right - left + 1)) + left],\r\n      i = left,\r\n      j = right\r\n  // 开始调整\r\n  while(i \u003c= j) {\r\n    \r\n    // 左指针右移\r\n    while(arr[i] \u003c datum) {\r\n      i++\r\n    }\r\n    \r\n    // 右指针左移\r\n    while(arr[j] \u003e datum) {\r\n      j--\r\n    }\r\n    \r\n    // 交换\r\n    if(i \u003c= j) {\r\n      swap(arr, i, j)\r\n      i += 1\r\n      j -= 1\r\n    }\r\n  }\r\n  return i\r\n}\r\n\r\n// 交换\r\nlet swap = (arr, i , j) =\u003e {\r\n    let temp = arr[i]\r\n    arr[i] = arr[j]\r\n    arr[j] = temp\r\n}\r\n\r\n// 测试\r\nlet arr = [1, 3, 2, 5, 4]\r\nquickSort(arr)\r\nconsole.log(arr) // [1, 2, 3, 4, 5]\r\n// 第 2 个最大值\r\nconsole.log(arr[arr.length - 2])  // 4\r\n```\r\n\r\n快排是从小到大排序,所以第 `k` 个最大值在 `n-k` 位置上\r\n\r\n**复杂度分析**\r\n\r\n- 时间复杂度:O(nlog~2~n)\r\n- 空间复杂度:O(nlog~2~n)\r\n\r\n#### 快速选择(quickselect)算法\r\n\r\n上面我们实现了快速排序来取第 k 个最大值,其实没必要那么麻烦,\r\n\r\n我们仅仅需要在每执行一次快排的时候,比较基准值位置是否在 `n-k` 位置上,\r\n\r\n- 如果小于 `n-k` ,则第 k 个最大值在基准值的右边,我们只需递归快排基准值右边的子序列即可;\r\n- 如果大于 `n-k` ,则第 k 个最大值在基准值的做边,我们只需递归快排基准值左边的子序列即可;\r\n- 如果等于  `n-k` ,则第 k 个最大值就是基准值\r\n\r\n**代码实现:**\r\n\r\n```js\r\nlet findKthLargest = function(nums, k) {\r\n    return quickSelect(nums, nums.length - k)\r\n};\r\n\r\nlet quickSelect = (arr, k) =\u003e {\r\n  return quick(arr, 0 , arr.length - 1, k)\r\n}\r\n\r\nlet quick = (arr, left, right, k) =\u003e {\r\n  let index\r\n  if(left \u003c right) {\r\n    // 划分数组\r\n    index = partition(arr, left, right)\r\n    // Top k\r\n    if(k === index) {\r\n        return arr[index]\r\n    } else if(k \u003c index) {\r\n        // Top k 在左边\r\n        return quick(arr, left, index-1, k)\r\n    } else {\r\n        // Top k 在右边\r\n        return quick(arr, index+1, right, k)\r\n    }\r\n  }\r\n  return arr[left]\r\n}\r\n\r\nlet partition = (arr, left, right) =\u003e {\r\n  // 取中间项为基准\r\n  var datum = arr[Math.floor(Math.random() * (right - left + 1)) + left],\r\n      i = left,\r\n      j = right\r\n  // 开始调整\r\n  while(i \u003c j) {\r\n    \r\n    // 左指针右移\r\n    while(arr[i] \u003c datum) {\r\n      i++\r\n    }\r\n    \r\n    // 右指针左移\r\n    while(arr[j] \u003e datum) {\r\n      j--\r\n    }\r\n    \r\n    // 交换\r\n    if(i \u003c j) swap(arr, i, j)\r\n\r\n    // 当数组中存在重复数据时,即都为datum,但位置不同\r\n    // 继续递增i,防止死循环\r\n    if(arr[i] === arr[j] \u0026\u0026 i !== j) {\r\n        i++\r\n    }\r\n  }\r\n  return i\r\n}\r\n\r\n// 交换\r\nlet swap = (arr, i , j) =\u003e {\r\n    let temp = arr[i]\r\n    arr[i] = arr[j]\r\n    arr[j] = temp\r\n}\r\n```\r\n\r\n**复杂度分析:**\r\n\r\n- 时间复杂度:平均时间复杂度O(n),最坏情况时间复杂度为O(n\u003csup\u003e2\u003c/sup\u003e)\r\n- 空间复杂度:O(1)\r\n\r\n### 六、解法:继续优化,中位数的中位数(BFPRT)算法\r\n\r\n又称为**中位数的中位数算法**,它的最坏时间复杂度为 O(n) ,它是由**Blum、Floyd、Pratt、Rivest、Tarjan**提出。该算法的思想是修改快速选择算法的主元选取方法,提高算法在最坏情况下的时间复杂度。\r\n\r\n在BFPTR算法中,仅仅是改变了快速选择(quickselect)算法中 `Partion` 中的基准值的选取,在快速选择(quickselect)算法中,我们可以选择第一个元素或者最后一个元素作为基准元,优化的可以选择随机一个元素作为基准元,而在 BFPTR 算法中,每次选择五分中位数的中位数作为基准元(也称为主元**pivot**),这样做的目的就是使得划分比较合理,从而避免了最坏情况的发生。\r\n\r\nBFPRT 算法步骤如下:\r\n\r\n- 选取主元\r\n  - 将 n 个元素按顺序分为 `n/5` 个组,每组 5 个元素,若有剩余,舍去\r\n  - 对于这 `n/5` 个组中的每一组使用插入排序找到它们各自的中位数\r\n  - 对于上一步中找到的所有中位数,调用 BFPRT 算法求出它们的中位数,作为主元;\r\n- 以主元为分界点,把小于主元的放在左边,大于主元的放在右边;\r\n- 判断主元的位置与 k 的大小,有选择的对左边或右边递归\r\n\r\n**代码实现:**\r\n\r\n```js\r\nlet findKthLargest = function(nums, k) {\r\n    return nums[bfprt(nums, 0, nums.length - 1, nums.length - k)]\r\n}\r\n\r\nlet bfprt = (arr, left , right, k) =\u003e {\r\n  let index\r\n  if(left \u003c right) {\r\n    // 划分数组\r\n    index = partition(arr, left, right)\r\n    // Top k\r\n    if(k === index) {\r\n        return index\r\n    } else if(k \u003c index) {\r\n        // Top k 在左边\r\n        return bfprt(arr, left, index-1, k)\r\n    } else {\r\n        // Top k 在右边\r\n        return bfprt(arr, index+1, right, k)\r\n    }\r\n  }\r\n  return left\r\n}\r\n\r\nlet partition = (arr, left, right) =\u003e {\r\n  // 基准\r\n  var datum = arr[findMid(arr, left, right)],\r\n      i = left,\r\n      j = right\r\n  // 开始调整\r\n  while(i \u003c j) {\r\n    // 左指针右移\r\n    while(arr[i] \u003c datum) {\r\n      i++\r\n    }\r\n    \r\n    // 右指针左移\r\n    while(arr[j] \u003e datum) {\r\n      j--\r\n    }\r\n    \r\n    // 交换\r\n    if(i \u003c j) swap(arr, i, j)\r\n\r\n    // 当数组中存在重复数据时,即都为datum,但位置不同\r\n    // 继续递增i,防止死循环\r\n    if(arr[i] === arr[j] \u0026\u0026 i !== j) {\r\n        i++\r\n    }\r\n  }\r\n  return i\r\n}\r\n\r\n/**\r\n * 数组 arr[left, right] 每五个元素作为一组,并计算每组的中位数,\r\n * 最后返回这些中位数的中位数下标(即主元下标)。\r\n *\r\n * @attention 末尾返回语句最后一个参数多加一个 1 的作用其实就是向上取整的意思,\r\n * 这样可以始终保持 k 大于 0。\r\n */\r\nlet findMid = (arr, left, right) =\u003e {\r\n    if (right - left \u003c 5)\r\n        return insertSort(arr, left, right);\r\n\r\n    let n = left - 1;\r\n\r\n    // 每五个作为一组,求出中位数,并把这些中位数全部依次移动到数组左边\r\n    for (let i = left; i + 4 \u003c= right; i += 5)\r\n    {\r\n        let index = insertSort(arr, i, i + 4);\r\n        swap(arr[++n], arr[index]);\r\n    }\r\n\r\n    // 利用 bfprt 得到这些中位数的中位数下标(即主元下标)\r\n    return findMid(arr, left, n);\r\n}\r\n\r\n/**\r\n * 对数组 arr[left, right] 进行插入排序,并返回 [left, right]\r\n * 的中位数。\r\n */\r\nlet insertSort = (arr, left, right) =\u003e {\r\n    let temp, j\r\n    for (let i = left + 1; i \u003c= right; i++) {\r\n        temp = arr[i];\r\n        j = i - 1;\r\n        while (j \u003e= left \u0026\u0026 arr[j] \u003e temp)\r\n        {\r\n            arr[j + 1] = arr[j];\r\n            j--;\r\n        }\r\n        arr[j + 1] = temp;\r\n    }\r\n    return ((right - left) \u003e\u003e 1) + left;\r\n}\r\n\r\n// 交换\r\nlet swap = (arr, i , j) =\u003e {\r\n    let temp = arr[i]\r\n    arr[i] = arr[j]\r\n    arr[j] = temp\r\n}\r\n```\r\n\r\n**复杂度分析:**\r\n\r\n![](http://resource.muyiy.cn/image/20200610020546.png)\r\n\r\n**为什么是5?**\r\n\r\n在BFPRT算法中,为什么是选5个作为分组?\r\n\r\n首先,偶数排除,因为对于奇数来说,中位数更容易计算。\r\n\r\n如果选用3,有 ![](http://resource.muyiy.cn/image/20200610020528.png),其操作元素个数还是 `n` 。\r\n\r\n如果选取7,9或者更大,在插入排序时耗时增加,常数 `c` 会很大,有些得不偿失。\r\n\r\n### 七、回顾与总结\r\n\r\n最后,我们总结一下,求topk问题其实并不难,主要有以下几个思路:\r\n\r\n- **整体排序**:O(nlogn)\r\n- **局部排序**:只冒泡排序前k个最大值,O(n*k)\r\n- **利用堆**:O(nlogk)\r\n- **计数或桶排序**:计数排序用于前k个最值,时间复杂度为O(n + m),其中 m 表示数据范围;桶排序用于最高频k个,时间复杂度为O(n); **但这两者都要求输入数据必须是有确定范围的整数** ,本文没有做过多介绍,可前往 \u003chttps://github.com/sisterAn/JavaScript-Algorithms\u003e 获取更多信息\r\n- **优化:快速选择(quickselect)算法**:平均O(n),最坏O(n\u003csup\u003e2\u003c/sup\u003e)\r\n- **继续优化:中位数的中位数(bfprt)算法**:最坏O(n)\r\n\r\n这里简单说一下后两种方案:\r\n\r\n- 首先,需要知道什么是快排,快排的过程简单的说只有三步:首先从序列中选取一个数作为基准数;将比这个数大的数全部放到它的右边,把小于或者等于它的数全部放到它的左边 (一次快排 `partition`);然后分别对基准的左右两边重复以上的操作,直到数组完全排序\r\n\r\n- 快速选择(quickselect)算法:快排会把所有的数据进行排序,而我们仅仅想要的是第 k 个最大值,所以 `quickselect` 对快排进行来优化:在每执行一次快排(`partition`)的时候,比较基准值位置是否在 `n-k` (第k大=第n-k小,n为数组长度)位置上,如果小于 `n-k` ,则第 k 个最大值在基准值的右边,我们只需递归快排基准值右边的子序列即可;如果大于 `n-k` ,则第 k 个最大值在基准值的做边,我们只需递归快排基准值左边的子序列即可;如果等于  `n-k` ,则第 k 个最大值就是基准值,平均时间复杂度O(n),最坏情况时间复杂度为O(n\u003csup\u003e2\u003c/sup\u003e),在最坏情况下,时间复杂度还是很高的\r\n\r\n- 中位数的中位数(bfprt)算法:该算法的思想是修改快速选择(`quickselect`)算法的主元选取方法,提高算法在最坏情况下的时间复杂度\r\n\r\n最后,附上Top K问题经典练习题,解题内容太多,前往git仓库查看解答\r\n\r\n- 腾讯\u0026字节等:最小的k个数:\r\n\r\n  https://github.com/sisterAn/JavaScript-Algorithms/issues/59\r\n\r\n- leetcode347:前 K 个高频元素:\r\n\r\n  https://github.com/sisterAn/JavaScript-Algorithms/issues/61\r\n\r\n- 字节\u0026leetcode215:数组中的第K个最大元素:\r\n\r\n  https://github.com/sisterAn/JavaScript-Algorithms/issues/62","author":{"url":"https://github.com/sisterAn","@type":"Person","name":"sisterAn"},"datePublished":"2020-06-30T13:43:35.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":1},"url":"https://github.com/73/JavaScript-Algorithms/issues/73"}

route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:5c41fe73-3184-23eb-7ab2-264b84ccc354
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-idEDEE:1CEAED:19B0D2A:21F211D:696AEE4F
html-safe-nonce49f1dffac60e06c1d8265cccbf7c756873eebf9ce850d032f490e0f721e50609
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJFREVFOjFDRUFFRDoxOUIwRDJBOjIxRjIxMUQ6Njk2QUVFNEYiLCJ2aXNpdG9yX2lkIjoiNjY2NTc2MjY0MjEzMzE4NDA3OSIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9
visitor-hmac78413e07393e20fe4bd64b791775fd1702c9db6aecd0d5b2816e4fbcb78f7bff
hovercard-subject-tagissue:648187087
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/sisterAn/JavaScript-Algorithms/73/issue_layout
twitter:imagehttps://opengraph.githubassets.com/a1c3e2e01546cbf2ed5d617b2b18aaa318c1213a6aa976edd3c6ba05fa1397a9/sisterAn/JavaScript-Algorithms/issues/73
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/a1c3e2e01546cbf2ed5d617b2b18aaa318c1213a6aa976edd3c6ba05fa1397a9/sisterAn/JavaScript-Algorithms/issues/73
og:image:alt引言 今天这篇文章讲解一道活跃于各个大厂(腾讯、字节、阿里等)面试中的题目:Top K 问题,将按照以下脉络介绍: 什么是 Top K 问题 Top K 问题的五种经典解法 快速回顾与总结 练习题 Top K 三剑客:最小 K 个数、前 K 个高频元素、第 K 个最小元素(含解答) 下面直接开始吧👇 一、什么是 Top K 问题 什么是 Top K 问题?简单来说就是在一组数据里面找到频率出...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernamesisterAn
hostnamegithub.com
expected-hostnamegithub.com
None5f99f7c1d70f01da5b93e5ca90303359738944d8ab470e396496262c66e60b8d
turbo-cache-controlno-preview
go-importgithub.com/sisterAn/JavaScript-Algorithms git https://github.com/sisterAn/JavaScript-Algorithms.git
octolytics-dimension-user_id19721451
octolytics-dimension-user_loginsisterAn
octolytics-dimension-repository_id252061924
octolytics-dimension-repository_nwosisterAn/JavaScript-Algorithms
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id252061924
octolytics-dimension-repository_network_root_nwosisterAn/JavaScript-Algorithms
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release82560a55c6b2054555076f46e683151ee28a19bc
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/sisterAn/JavaScript-Algorithms/issues/73#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FsisterAn%2FJavaScript-Algorithms%2Fissues%2F73
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub SparkBuild and deploy intelligent appshttps://github.com/features/spark
GitHub ModelsManage and compare promptshttps://github.com/features/models
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
GitHub SponsorsFund open source developershttps://github.com/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/accelerator
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FsisterAn%2FJavaScript-Algorithms%2Fissues%2F73
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=sisterAn%2FJavaScript-Algorithms
Reloadhttps://github.com/sisterAn/JavaScript-Algorithms/issues/73
Reloadhttps://github.com/sisterAn/JavaScript-Algorithms/issues/73
Reloadhttps://github.com/sisterAn/JavaScript-Algorithms/issues/73
sisterAn https://github.com/sisterAn
JavaScript-Algorithmshttps://github.com/sisterAn/JavaScript-Algorithms
Notifications https://github.com/login?return_to=%2FsisterAn%2FJavaScript-Algorithms
Fork 649 https://github.com/login?return_to=%2FsisterAn%2FJavaScript-Algorithms
Star 5.7k https://github.com/login?return_to=%2FsisterAn%2FJavaScript-Algorithms
Code https://github.com/sisterAn/JavaScript-Algorithms
Issues 158 https://github.com/sisterAn/JavaScript-Algorithms/issues
Pull requests 0 https://github.com/sisterAn/JavaScript-Algorithms/pulls
Actions https://github.com/sisterAn/JavaScript-Algorithms/actions
Projects 0 https://github.com/sisterAn/JavaScript-Algorithms/projects
Security Uh oh! There was an error while loading. Please reload this page. https://github.com/sisterAn/JavaScript-Algorithms/security
Please reload this pagehttps://github.com/sisterAn/JavaScript-Algorithms/issues/73
Insights https://github.com/sisterAn/JavaScript-Algorithms/pulse
Code https://github.com/sisterAn/JavaScript-Algorithms
Issues https://github.com/sisterAn/JavaScript-Algorithms/issues
Pull requests https://github.com/sisterAn/JavaScript-Algorithms/pulls
Actions https://github.com/sisterAn/JavaScript-Algorithms/actions
Projects https://github.com/sisterAn/JavaScript-Algorithms/projects
Security https://github.com/sisterAn/JavaScript-Algorithms/security
Insights https://github.com/sisterAn/JavaScript-Algorithms/pulse
New issuehttps://github.com/login?return_to=https://github.com/sisterAn/JavaScript-Algorithms/issues/73
New issuehttps://github.com/login?return_to=https://github.com/sisterAn/JavaScript-Algorithms/issues/73
前端进阶算法10:别再说你不懂topk问题了https://github.com/sisterAn/JavaScript-Algorithms/issues/73#top
https://github.com/sisterAn
https://github.com/sisterAn
sisterAnhttps://github.com/sisterAn
on Jun 30, 2020https://github.com/sisterAn/JavaScript-Algorithms/issues/73#issue-648187087
https://camo.githubusercontent.com/4893b58af5cc857eb140beab32013b35da2f2bcef84785bde60f62d6e4b81ba3/687474703a2f2f7265736f757263652e6d757969792e636e2f696d6167652f32303230303632393030343231312e6a7067
https://camo.githubusercontent.com/a1f42274a3dd022363e1b4e4428ff30dffd0dc4645db5f0b4dbd35374877cab6/687474703a2f2f7265736f757263652e6d757969792e636e2f696d6167652f32303230303632383233353533302e676966
https://camo.githubusercontent.com/8b0d432d59897fe367bf51e78b2d778c9e68ea1d53694021fc9eb3294f47bc86/687474703a2f2f7265736f757263652e6d757969792e636e2f696d6167652f32303230303632383233313835302e706e67
https://camo.githubusercontent.com/95d19ba9f3b250010fa3c0e403ce0332d9066225be455016d3076e737dd9350b/687474703a2f2f7265736f757263652e6d757969792e636e2f696d6167652f32303230303632383233313932372e706e67
前端进阶算法9:看完这篇,再也不怕堆排序、Top K、中位数问题面试了https://github.com/sisterAn/JavaScript-Algorithms/issues/60
https://camo.githubusercontent.com/861fb262b8adc434fe9bee9e415e65b8eb6105b0bab9d691fcc9cb1bc62acd90/687474703a2f2f7265736f757263652e6d757969792e636e2f696d6167652f32303230303632373232343335352e676966
https://camo.githubusercontent.com/19bb056b27c981162b62bd52097af6431f2fd7f722824dd565b5f0637557b030/687474703a2f2f7265736f757263652e6d757969792e636e2f696d6167652f32303230303631303032303534362e706e67
https://camo.githubusercontent.com/1753a9d5dfcc7a32094c41c4c951c05b84bcef83c1dca035b0469b3f925af6dd/687474703a2f2f7265736f757263652e6d757969792e636e2f696d6167652f32303230303631303032303532382e706e67
https://github.com/sisterAn/JavaScript-Algorithmshttps://github.com/sisterAn/JavaScript-Algorithms
腾讯&字节等:最小的k个数 #59https://github.com/sisterAn/JavaScript-Algorithms/issues/59
leetcode347:前 K 个高频元素 #61https://github.com/sisterAn/JavaScript-Algorithms/issues/61
字节&leetcode215:数组中的第K个最大元素 #62https://github.com/sisterAn/JavaScript-Algorithms/issues/62
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.