文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

每日算法:数据流的中位数

2024-12-02 18:42

关注

本文转载自微信公众号「三分钟学前端」,作者sisterAn  。转载本文请联系三分钟学前端公众号。

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

[2,3] 的中位数是 (2 + 3) / 2 = 2.5

设计一个支持以下两种操作的数据结构:

示例:

  1. addNum(1) 
  2. addNum(2) 
  3. findMedian() -> 1.5 
  4. addNum(3)  
  5. findMedian() -> 2 

进阶:

看到这个动态数组获取中位数问题,不要太激动,这太适合使用堆了,考察的就是堆的经典应用:中位数问题,详情可查看 前端进阶算法9:看完这篇,再也不怕堆排序、Top K、中位数问题面试了

解法:利用堆

解题思路:

这里需要维护两个堆:

那么,根据题目要求,中位数就为:

当数组为动态数组时,每当数组中插入一个元素时,都需要如何调整堆喃?

如果插入元素比大顶堆的堆顶要大,则将该元素插入到小顶堆中;如果要小,则插入到大顶堆中。

当插入完成后,如果大顶堆、小顶堆中元素的个数不满足我们已上的要求,我们就需要不断的将大顶堆的堆顶元素或小顶堆的堆顶元素移动到另一个堆中,直到满足要求

代码实现:

  1. let MedianFinder = function() { 
  2.     // 大顶堆,用来保存前 n/2 小的元素 
  3.     this.lowHeap = new MaxHeap() 
  4.     // 小顶堆,用来保存后 n/2 小的元素 
  5.     this.hightHeap = new MinHeap() 
  6. }; 
  7. // 插入元素 
  8. MedianFinder.prototype.addNum = function(num) { 
  9.     // 如果大顶堆为空或大顶堆堆顶元素小于num,则插入大顶堆 
  10.     // 否则插入到小顶堆中 
  11.     if(!this.lowHeap.getSize() || num < this.lowHeap.getHead()) { 
  12.         // 比大顶堆的堆顶小,插入到大顶堆中 
  13.         this.lowHeap.insert(num) 
  14.     } else { 
  15.         // 比小顶堆的堆顶大,插入到小顶堆中 
  16.         this.hightHeap.insert(num) 
  17.     } 
  18.  
  19.     // 比较大小顶堆的是否依然保持平衡 
  20.     if(this.lowHeap.getSize() - this.hightHeap.getSize() > 1) { 
  21.         // 大顶堆往小顶堆迁移 
  22.         this.hightHeap.insert(this.lowHeap.removeHead()) 
  23.     } 
  24.     if(this.hightHeap.getSize() > this.lowHeap.getSize()) { 
  25.         // 小顶堆向大顶堆迁移 
  26.         this.lowHeap.insert(this.hightHeap.removeHead()) 
  27.     } 
  28. }; 
  29. // 获取中位数 
  30. MedianFinder.prototype.findMedian = function() { 
  31.     if(this.lowHeap.getSize() && this.lowHeap.getSize() === this.hightHeap.getSize()) { 
  32.         return (this.lowHeap.getHead() + this.hightHeap.getHead())/2 
  33.     } 
  34.     return this.lowHeap.getHead() 
  35. }; 

其中小顶堆定义:

  1. // 小顶堆 
  2. let MinHeap = function() { 
  3.     let heap = [,] 
  4.     // 堆中元素数量 
  5.     this.getSize = ()=> heap.length - 1 
  6.     // 插入 
  7.     this.insert = (key) => { 
  8.         heap.push(key
  9.         // 获取存储位置 
  10.         let i = heap.length-1 
  11.         while (Math.floor(i/2) > 0 && heap[i] < heap[Math.floor(i/2)]) {   
  12.             swap(heap, i, Math.floor(i/2)); // 交换  
  13.             i = Math.floor(i/2);  
  14.         } 
  15.     } 
  16.     // 删除堆头并返回 
  17.     this.removeHead = () => { 
  18.         if(heap.length > 1) { 
  19.             if(heap.length === 2) return heap.pop() 
  20.             let num = heap[1] 
  21.             heap[1] = heap.pop() 
  22.             heapify(1) 
  23.             return num 
  24.         } 
  25.         return null 
  26.     } 
  27.     // 获取堆头 
  28.     this.getHead = () => { 
  29.         return heap.length > 1 ? heap[1]:null 
  30.     } 
  31.     // 堆化 
  32.     let heapify = (i) => { 
  33.         let k = heap.length-1 
  34.         // 自上而下式堆化 
  35.         while(true) { 
  36.             let minIndex = i 
  37.             if(2*i <= k && heap[2*i] < heap[i]) { 
  38.                 minIndex = 2*i 
  39.             } 
  40.             if(2*i+1 <= k && heap[2*i+1] < heap[minIndex]) { 
  41.                 minIndex = 2*i+1 
  42.             } 
  43.             if(minIndex !== i) { 
  44.                 swap(heap, i, minIndex) 
  45.                 i = minIndex 
  46.             } else { 
  47.                 break 
  48.             } 
  49.         } 
  50.     }  
  51.     let swap = (arr, i, j) => { 
  52.         let temp = arr[i] 
  53.         arr[i] = arr[j] 
  54.         arr[j] = temp 
  55.     } 

大顶堆定义:

  1. // 大顶堆 
  2. let MaxHeap = function() { 
  3.     let heap = [,] 
  4.     // 堆中元素数量 
  5.     this.getSize = ()=>heap.length - 1 
  6.     // 插入大顶堆 
  7.     this.insert = (key) => { 
  8.         heap.push(key
  9.         // 获取存储位置 
  10.         let i = heap.length-1 
  11.         while (Math.floor(i/2) > 0 && heap[i] > heap[Math.floor(i/2)]) {   
  12.             swap(heap, i, Math.floor(i/2)); // 交换  
  13.             i = Math.floor(i/2);  
  14.         } 
  15.     } 
  16.     // 获取堆头 
  17.     this.getHead = () => { 
  18.         return heap.length > 1 ? heap[1]:null 
  19.     } 
  20.     // 删除堆头并返回 
  21.     this.removeHead = () => { 
  22.         if(heap.length > 1) { 
  23.             if(heap.length === 2) return heap.pop() 
  24.             let num = heap[1] 
  25.             heap[1] = heap.pop() 
  26.             heapify(1) 
  27.             return num 
  28.         } 
  29.         return null 
  30.     } 
  31.     // 堆化 
  32.     let heapify = (i) => { 
  33.         let k = heap.length-1 
  34.         // 自上而下式堆化 
  35.         while(true) { 
  36.             let maxIndex = i 
  37.             if(2*i <= k && heap[2*i] > heap[i]) { 
  38.                 maxIndex = 2*i 
  39.             } 
  40.             if(2*i+1 <= k && heap[2*i+1] > heap[maxIndex]) { 
  41.                 maxIndex = 2*i+1 
  42.             } 
  43.             if(maxIndex !== i) { 
  44.                 swap(heap, i, maxIndex) 
  45.                 i = maxIndex 
  46.             } else { 
  47.                 break 
  48.             } 
  49.         } 
  50.     }  
  51.     let swap = (arr, i, j) => { 
  52.         let temp = arr[i] 
  53.         arr[i] = arr[j] 
  54.         arr[j] = temp 
  55.     } 

复杂度分析:

时间复杂度:由于插入元素到堆的时间复杂度为 O(logn),为树的高度;移动堆顶元素都需要堆化,时间复杂度也为O(logn);所以,插入( addNum )的时间复杂度为 O(logn) ,每次插入完成后求中位数仅仅需要返回堆顶元素即可, findMedian 时间复杂度为 O(1)

空间复杂度:O(n)

如果数据流中所有整数都在 0 到 100 范围内,我们可以尝试使用计数排序,但计数排序的时间复杂度是O(n + m),其中 m 表示数据范围,复杂度较高,这里不适合,计数排序比较适合静态数组前k个最值问题 leetcode347:前 K 个高频元素

leetcode:https://leetcode-cn.com/problems/find-median-from-data-stream/solution/javascriptshu-ju-liu-de-zhong-wei-shu-by-user7746o/

 

来源:三分钟学前端内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯