一、题目描述
JZ63 数据流中的中位数
题目大意:设计一个类,它有两个方法,Insert(num)可以插入一个数num,GetMedian()返回所有插入的数中的中位数(若一共插入了偶数个,则取中间两个数的平均值;若一共插入了奇数个,则取中间一个即可)
二、算法1(暴力法)
解题思路
用一个数组来存放所有数,每次GetMedian之前都排序一下,再根据数组的大小确定中位数
代码实现(C++11)
class Solution { public: vector<int> arr; void Insert(int num) { arr.push_back(num); } double GetMedian() { sort(arr.begin(), arr.end()); // 从小到大排序 int n = arr.size(); if(n % 2 == 0){ // 若数组大小为偶数 return (arr[n / 2] + arr[(n / 2) - 1]) / 2.0; } return (double)arr[n / 2]; // 为奇数则返回中间的数 } };
时间复杂度:O(m + knlogn), m为Insert执行的次数,一次Insert操作的时间复杂度是O(logn),n为插入的元素数量,k为GetMedian执行的次数,一次GetMedian的时间复杂度是O(nlogn)
空间复杂度:O(n),n为插入元素的数量
三、算法2(对顶堆)
解题思路
- 本题是对顶堆算法的一个经典题,所谓对顶堆算法,实际上就是使用一个小顶堆和一个大顶堆
- 对于求动态中位数的问题,我们可以利用堆的性质,可以在logn的时间复杂度取出最大值或最小值,那么我们可以用大顶堆维护前一半的数,用小顶堆维护后面一半的数
- 我们规定,若元素总数为奇数,那么大顶堆的元素个数要比小顶对的个数多1,这样,当总数为奇数时,中位数就是大顶堆的堆顶元素;当总数为偶数时,中位数就大顶堆堆顶和小顶堆堆顶元素的平均值
代码实现(C++11)
class Solution { public: priority_queue<int> max_heap; // 大顶堆 priority_queue<int, vector<int>, greater<int>> min_heap; // 小顶堆 void Insert(int num) { if(max_heap.empty() || max_heap.top() > num) max_heap.push(num); // 大顶堆为空或大顶堆堆顶大于插入元素 else min_heap.push(num); // 否则加入小顶堆 // size()方法得到的类型size_type是无符号的, 切记不要直接放在判断语句中, 要先转成int型 int a = max_heap.size(), b = min_heap.size(); if(a - b > 1){ // 大顶堆元素数量过多,保证大顶堆大小-小顶堆大小大于等于1 int p = max_heap.top(); max_heap.pop(); min_heap.push(p); } else if(b > a){ // 小顶堆元素数量过多 int p = min_heap.top(); min_heap.pop(); max_heap.push(p); } } double GetMedian() { if(max_heap.si***_heap.size()){ // 偶数长度,返回两数的平均值 return (max_heap.top() + min_heap.top()) / 2.0; } return (double)max_heap.top(); // 奇数长度,返回大顶堆堆顶 } };
时间复杂度:O(mlogn + k),m为Insert执行的次数,一次Insert操作的时间复杂度是O(logn),n为插入的元素数量,k为GetMedian执行的次数,一次GetMedian的时间复杂度是O(1)
空间复杂度:O(n),n为插入的元素数量