题目:
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
思路解析
数据是从数据流读出,因此数组的个数是再逐渐的增加。如何选用一个容器,能够存储数据,并能够给出中位数。
- 无序数组:插入O(1) partation操作找出中位数 O(n)
- 有序数组:插入O(n) 找出中位数O(1)
- 有序链表:插入O(n) 找出中位数O(1)
- 搜索二叉树:插入O(logn)~O(n),找出中位数O(logn)~O(n)
- AVL:插入O(logn),在节点中添加计数信息,找出中位数O(1)
如果将容器中的数据按照顺序排列,那么最中间的两个数或者一个数,可以将数据平均分成2部分。
那么以偶数个数据节点为例:最中间的两个数中的左侧数字,是前一半数据的最大值;中间数右侧数字,是后一半数据的最小值。因此只要保证,数据能够平均分成2部分,前一部分都小于后一部分,那么中位数是前一部分的最大值和后一部分的最小值的平均值。
为了实现O(1)得到最大最小值,采用最大堆保存前一半数据,最小堆保存后一半数据
为了实现平均:当容器中数据个数是偶数:插入到最小堆;总数是奇数,插入到最大堆。
当总数是偶数时,应该插入最小堆,但如果此时的数据小于最大堆的最大值,即按值大小来说,它应该属于前一部分(最大堆);因此我们将该值插入到最大堆,然后取出最大堆中的最大值,插入到最小堆。
同理,奇数时,数据大于最小堆的最小值,也应该先如最小堆,将最小堆中的最小值,移动到最大堆。
关于 C++ STL 堆的知识:
heap 的头文件 #include<algorithm>
注意:_First, _Last 是容器的迭代器,如vector。
参考:c++中STL之heap, priority_queue使用
1. make_heap
make_heap(_First, _Last)
make_heap(_First, _Last, _Comp) // 对2个迭代器之间的元素建堆
默认是建立最大堆的。对int类型,可以在第三个参数传入greater<int>()
得到最小堆。
2. push_heap
push_heap(_First, _Last) // 首先在容器中添加一个元素,然后push_heap,处理最后添加的一个元素
新添加一个元素在末尾,然后重新调整堆序。也就是把元素添加在底层vector的end()处。每次只能处理 1 个。
3. pop_heap
pop_heap(_First, _Last) // 先pop_heap,然后在容器中pop
这个算法跟push_heap类似,参数一样。不同的是我们把堆顶元素取出来,放到了数组或者是vector的末尾,用原来末尾元素去替代,然后end迭代器减1
4. sort_heap
.sort_heap(_First, _Last) // 堆排序
template<class T>
class DynamicArray {
private:
vector<T> max; // 数组的前一半一半最大堆,最大堆中的数全部小于最小堆
vector<T> min; // 数组的后一半构成最小堆,
public:
void Insert(T num) {
// 已有元素总数是偶数,插入最小堆
if (((min.size() + max.size()) & 0x01) == 0) {
if (max.size() > 0 && num < max[0]) {
// num 小于最小堆的最小值,按值看应该插入最大堆,因此,先插入max,将max中的最大值,移动到min
max.push_back(num);
push_heap(max.begin(), max.end(), less<T>());
num = max[0]; // 取出最大堆的最大值,插入到最小堆
pop_heap(max.begin(), max.end(), less<T>());
max.pop_back();
}
min.push_back(num);
push_heap(min.begin(), min.end(), greater<T>());
} else {
// 已有元素总数是奇数,插入最大堆
if (min.size() > 0 && num > min[0]) {
// num 大于最大堆的最大值,按值看应该插入最小堆
min.push_back(num);
push_heap(min.begin(), min.end(), greater<T>());
num = min[0];
pop_heap(min.begin(), min.end(), greater<T>());
min.pop_back();
}
max.push_back(num);
push_heap(max.begin(), max.end(), less<T>());
}
};
T GetMedian() {
int size = min.size() + max.size();
if (size == 0)
throw exception("No numbers are avliable");
if ((size & 0x01) == 1)
return min[0];
else
return (min[0]+max[0])/2;
}
};
测试案例:《剑指offer》 GitHub
版权声明:本文为博主原创文章,未经博主允许不得转载。