题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
(1)最简单的方法,使用工具类对其进行排序,然后取其中位数即可。
import java.util.*;
public class Solution {
ArrayList<Integer> arr = new ArrayList();
public void Insert(Integer num) {
arr.add(num);
}
public Double GetMedian() {
Collections.sort(arr);
int len = arr.size();
if(len % 2 == 0){
return ((double)arr.get(len/2) + (double)arr.get(len/2-1)) / 2;
}else
return (double)arr.get(len/2);
}
}
(2)然后,使用快排划分的算法;
如果数组没有排序,那么可以用partition函数找到数组中的中位数。
import java.util.*;
public class Solution {
ArrayList<Integer> arr = new ArrayList();
public void Insert(Integer num) {
arr.add(num);
}
public Double GetMedian() {
int low = 0;
int high = arr.size() - 1;
int index = partition(arr,low,high);
int mid = arr.size() / 2;
while(mid != index){
if(index > mid)
index = partition(arr,low,index - 1);
else
index = partition(arr,index+1,high);
}
int res1 = arr.get(index);
if(arr.size() % 2 == 0){
mid = arr.size() / 2 - 1;
while(mid != index){
if(index > mid)
index = partition(arr,low,index - 1);
else
index = partition(arr,index+1,high);
}
int res2 = arr.get(index);
return ((double)res1 + res2) / 2;
}else
return (double)res1;
}
public int partition( ArrayList<Integer> input,int low,int high){
int val = input.get(low);
while(low < high){
while(low<high && input.get(high)>=val)
high--;
input.set(low,input.get(high));
while(low<high && input.get(low)<val)
low++;
input.set(high,input.get(low));
}
input.set(low,val);
return low;
}
}
3、=》在插入数据的时候使容器数据保持排序。==》链表O(n)、二叉排序树O(logn)、AVL、堆
中位数把容器数据分为两部分:左边最大的数、右边最小的数==》中位数。
(1)使用大小堆原理:中位数把数组分为了两部分,根据最大堆获得左边部分最大的数,最小堆获得右边部分最小的数。=》可得中位数
(2)用最大堆实现左边的容器,最小堆实现右边的容器。插入时间效率O(logn),得到堆顶的时间效率是O(1)。
(3)得保证两个堆的数据数目的差不能超过1。为了实现平均分配,可以将数据总数是偶数时,把新的数插入到右边的最小堆;总数为奇数时,把新数插入到左边的最大堆。
(4)还得保证最大堆的所有数据都要小于最小堆的数据。==》当总数为奇数时,要插入最大堆,那么先把它插入最小堆,然后取最小堆的堆顶–最小值,然后再插入到最大堆(最终插入到最大堆的是原最小堆的最小元素)。偶数时,同理!
import java.util.*;
public class Solution {
int count = 0;
PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(11,new Comparator<Integer>(){
public int compare(Integer o1,Integer o2){
return o2 - o1;
}
});
public void Insert(Integer num) {
if(count % 2 == 0){
maxHeap.offer(num);
int filterMaxNum = maxHeap.poll();
minHeap.offer(filterMaxNum);
}else{
minHeap.offer(num);
int filterMinNum = minHeap.poll();
maxHeap.offer(filterMinNum);
}
count++;
}
public Double GetMedian() {
if(count % 2 == 0)
return (Double.valueOf(minHeap.peek()) + maxHeap.peek())/2;
else
return Double.valueOf(minHeap.peek());
}
}
参考:https://www.nowcoder.com/profile/645151/codeBookDetail?submissionId=1521636
内容如下:
讲解很详细
public void Insert(Integer num) {
if (count %2 == 0) {//当数据总数为偶数时,新加入的元素,应当进入小根堆
//(注意不是直接进入小根堆,而是经大根堆筛选后取大根堆中最大元素进入小根堆)
//1.新加入的元素先入到大根堆,由大根堆筛选出堆中最大的元素
maxHeap.offer(num);
int filteredMaxNum = maxHeap.poll();
//2.筛选后的【大根堆中的最大元素】进入小根堆
minHeap.offer(filteredMaxNum);
} else {//当数据总数为奇数时,新加入的元素,应当进入大根堆
//(注意不是直接进入大根堆,而是经小根堆筛选后取小根堆中最大元素进入大根堆)
//1.新加入的元素先入到小根堆,由小根堆筛选出堆中最小的元素
minHeap.offer(num);
int filteredMinNum = minHeap.poll();
//2.筛选后的【小根堆中的最小元素】进入大根堆
maxHeap.offer(filteredMinNum);
}
count++;
}
该题具有动态性,取决于使用的数据结构:
(1)无序数组—–插入O(1),查找O(n);
(2)有序数组/链表—插入O(n),查找O(1) 链表定义两个指针??
(3)二叉排序树—插入【平均O(logn),最差O(n)】
查找【平均O(logn),最差O(n)】
(4)AVL O(logn),O(1)
(5)最大最小堆:O(logn),O(1)