【剑指offer】面试题41:数据流的中位数
2017-07-31 14:15
232 查看
题目
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
思路
这个题目跟一般的固定数目的数组查找中位数不一样,它是数据流。由于数据是从数据流读出来的,所以数据的数量会随着时间变化而增加。
public class Solution { public void insert(Integer num) { // TODO } public Double getMedian() { // TODO } }
我们要做的就是实现
insert和
getMedian这两个函数
这道题会有很多种思路,这里就不一一细说了,把所有解法的时间复杂度列出来:
数据结构 | 插入的时间复杂度 | 得到中位数的时间复杂度 |
---|---|---|
没有排序的数组 | O(1) | O(n) |
排序的数组 | O(n) | O(1) |
排序的链表 | O(n) | O(1) |
二叉搜索树 | 平均O(logn),最差O(n) | 平均O(logn),最差O(n) |
AVL树 | O(logn) | O(1) |
最大堆和最小堆 | O(logn) | O(1) |
思路在代码注释里↓↓↓
代码(解法1,优先队列)
严重吐槽,牛客网在优先队列挂了我好多次了,好像不让用这个类!!??有关优先队列的我后来都用红黑树做了(解法2)
/** * 题目: * 如何得到一个数据流中的中位数? * 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。 * 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 * * 思路: * 用 一个最大堆 + 一个最小堆 来解决 * 如果能够保持 最大堆里的所有元素 < 最小堆里面的所有元素 * 如果元素总个数为奇数,最大堆元素个数=最小堆元素个数-1,则最小堆的顶部元素为中位数; * 如果元素总个数为偶数,最大堆元素个数=最小堆元素个数,最中位数=(最小堆的顶部元素+最大堆的顶部元素)/2 * * 解法: * 1.当插入第偶数个元素时(从第0个开始),将元素插入最大堆后,将最大堆顶部元素放到最小堆里 * 2.当插入第奇数个元素时,将元素插入最小堆后,将最小堆顶部元素放到最大堆里 * * 所以当插入奇数个元素时,中位数为最小堆顶部元素; * 当插入偶数个元素时,中位数为(最大堆顶部元素+最小堆顶部元素)/ 2 * * @author peige */ public class _41_StreamMedian_01 { PriorityQueue<Integer> maxQ = new PriorityQueue<>(Collections.reverseOrder()); PriorityQueue<Integer> minQ = new PriorityQueue<>(); public void Insert(Integer num) { if(((maxQ.size() + minQ.size()) & 1) == 0) { maxQ.offer(num); minQ.offer(maxQ.remove()); } else { minQ.offer(num); maxQ.offer(minQ.remove()); } } public Double GetMedian() { if(maxQ.size() == 0 && minQ.size() == 0) return new Double(0.0); if(((maxQ.size() + minQ.size()) & 1) == 0) { return (double)(minQ.peek() + maxQ.peek()) / 2; } else { return (double)(minQ.peek()); } } }
解法2(红黑树)
/** * 跟使用优先队列的思路一样,这里就不写了 * 牛客网优先队列编译不过,我也是醉了。 * * @author peige */ public class _41_StreamMedian_02 { TreeSet<Integer> maxQ = new TreeSet<>(Collections.reverseOrder()); TreeSet<Integer> minQ = new TreeSet<>(); public void Insert(Integer num) { if(((maxQ.size() + minQ.size()) & 1) == 0) { maxQ.add(num); minQ.add(maxQ.pollFirst()); } else { minQ.add(num); maxQ.add(minQ.pollFirst()); } } public Double GetMedian() { if(maxQ.size() == 0 && minQ.size() == 0) return new Double(0.0); if(((maxQ.size() + minQ.size()) & 1) == 0) { return (double)(minQ.first() + maxQ.first()) / 2; } else { return (double)(minQ.first()); } } }
测试(这里放的红黑树的,解法1的也一样)
public class _41_02_Test { public static void main(String[] args) { test1(); test2(); } /** * 功能测试 */ private static void test1() { _41_StreamMedian_02 sm = new _41_StreamMedian_02(); for(int i = 0; i < 10; ++i) { sm.Insert(i); System.out.print(sm.GetMedian() + " "); } System.out.println(); } /** * 极端测试 * 1.没有元素,要获得中位数 */ private static void test2() { _41_StreamMedian_02 sm = new _41_StreamMedian_02(); System.out.println(sm.GetMedian()); } }
相关文章推荐
- 剑指Offer-41:数据流中的中位数
- 剑指offer-面试题64-数据流中的中位数
- 剑指offer 面试题64 数据流中的中位数
- 【剑指Offer学习】【面试题64:数据流中的中位数】
- 剑指offer——面试题64:数据流中的中位数
- 剑指offer面试题64 数据流中的中位数(Java实现)
- 【剑指Offer】面试题64:数据流中的中位数
- 剑指offer-面试题64:数据流中的中位数
- 295. Find Median from Data Stream 剑指offer 数据流中的中位数
- 【剑指Offer】面试题41:和为S的两个数
- 剑指offer面试题41_2 和为s的连续正数序列(java实现)
- 【剑指offer】面试题41:和为S的连续子序列
- 剑指offer — 数据流中的中位数
- 剑指offer 64-数据流中的中位数
- 【剑指offer】面试题41-和为S的两个数VS和为S的连续正数序列
- 【剑指Offer】面试题41:和为s的连续正数序列
- 剑指offer—数据流中的中位数
- 剑指offer-----数据流的中位数(java版)
- 剑指Offer_面试题41_和为s的两个数字 VS 和为s的连续正数序列
- [剑指offer][面试题41]和为s的两个数字 VS 和为s的连续正数序列