题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
用两个堆,一个大顶堆,一个小顶堆。
先往小顶堆里面存数,并保持: 0 <= 小顶堆的size()-大顶堆的size() <= 1
保持两边数量几乎一致就需要在插入的时候进行比较、调整。
返回中位数的时候,如果小顶堆和大顶堆size()相同,就返回他们堆顶元素的平均值;否则返回小顶堆的堆顶元素。
这种方法插入时间复杂度是O(log n),返回中位数的时间复杂度是O(1)
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
//大顶堆
PriorityQueue<Integer> left = new PriorityQueue<Integer>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o2-o1;
}
}
);
//小顶堆
PriorityQueue<Integer> right = new PriorityQueue<Integer>();
public void Insert(Integer num) {
if(left.size() < right.size()){
if(right.peek()<num){
right.offer(num);
left.offer(right.poll());
}else{
left.offer(num);
}
}else{
left.offer(num);
right.offer(left.poll());
}
}
public Double GetMedian() {
if(left.size() == right.size())
return (double)(left.peek() + right.peek())/2;
else
return (double)(right.peek());
}
}
使用自制堆:
import java.util.Arrays;
import java.util.Comparator;
public class Solution {
//大顶堆
Heap<Integer> left = new Heap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
//小顶堆
Heap<Integer> right = new Heap<Integer>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
public void Insert(Integer num) {
if(left.size() < right.size()){
if(right.peek()<num){
right.offer(num);
left.offer(right.poll());
}else{
left.offer(num);
}
}else{
left.offer(num);
right.offer(left.poll());
}
}
public Double GetMedian() {
if(left.size() == right.size())
return (double)(left.peek() + right.peek())/2;
else
return (double)(right.peek());
}
private final static class Node<E> {
public E val;
public Node(E val) {
this.val = val;
}
@Override
public String toString() {
return val+"";
}
}
public class Heap<E>{
/**
* 当前序号
*/
private int curIndex = -1;
//当前数组容量
private int capcity = 10;
//用于存放元素的数组
private Node<E>[] arr = new Node[capcity];
//扩容比例
private int ratio = 2;
//比较器,从而决定堆的形式
private Comparator<E> comparator;
public Heap(Comparator<E> comparator){
this.comparator = comparator;
}
public void offer(E val) {
Node<E> node = new Node<E>(val);
if(++curIndex==capcity){
arr = Arrays.copyOf(arr, capcity*2);
capcity = arr.length;
}
arr[curIndex] = node;
heapUp(curIndex);
}
/**
* 子元素上升
*/
private void heapUp(int index) {
//如果是堆顶,则不上升
while(index > 0){
//父节点序号
int parentIndex = (index-1)/2;
//比较
if(comparator.compare(arr[index].val,arr[parentIndex].val)>0){
swap(parentIndex,index);
index = parentIndex;
}else{
break;
}
}
}
/**
* 交换父子元素
* @param parent
* @param child
*/
private void swap(int parent,int child) {
Node<E> parentNode = arr[parent];
arr[parent] = arr[child];
arr[child] = parentNode;
}
public void delete(int index) {
//将最后一个元素,赋值到被删除元素的位置
arr[index] = arr[curIndex];
//删除最后一个元素
arr[curIndex] = null;
curIndex--;
heapDown(index);
}
public void delete(E val) {
for(int i=0;i<=curIndex;i++){
if(val==arr[i].val){
delete(i);
return;
}
}
}
/**
* 子元素下降
* @param index
*/
private void heapDown(int index) {
//如果超出curIndex,则已经沉到底了
while(index<curIndex){
//父节点,左右子节点中的最大值
int maxIndex = index;
//左子节点
int leftChildIndex = index*2+1;
//如果存在左子节点
if(leftChildIndex<=curIndex){
maxIndex = comparator.compare(arr[leftChildIndex].val, arr[index].val)>0?leftChildIndex:index;
}
//右子节点
int rightChildIndex = index*2+2;
//如果存在右子节点
if(rightChildIndex<=curIndex){
maxIndex = comparator.compare(arr[rightChildIndex].val, arr[maxIndex].val)>0?rightChildIndex:maxIndex;
}
//如果子节点比父节点大,那么将父节点下沉
if(maxIndex!=index){
swap(maxIndex, index);
//继续下沉节点
index = maxIndex;
}else{
return;
}
}
}
public E poll() {
if(isEmpty()){
return null;
}
E val = arr[0].val;
delete(0);
return val;
}
public E peek() {
if(isEmpty()){
return null;
}
return arr[0].val;
}
public boolean isEmpty() {
return curIndex == -1;
}
public int size(){
return curIndex+1;
}
@Override
public String toString() {
return Arrays.toString(arr);
}
}
}