52.数据流中的中位数(214)
-
题目描述:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
-
思路:(1)使用快排的分区函数找出中位数。(2)利用最大堆和最小堆。
-
代码:
package _52.数据流中的中位数; import java.util.LinkedList; /** * 如何得到一个数据流中的中位数? * 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。 * 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 * 我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。 * @author Administrator * */ public class StreamMedian { private static LinkedList<Integer> list = new LinkedList<>(); //向数据流中插入数据 public static void Insert(Integer num) { list.add(num); } //获取数据流中的中位数 public static Double GetMedian() { if(list.size() == 0) return -1.0; int left = 0; int right = list.size() - 1; int index = paritition(left,right); int mid = list.size() >>> 1; while(index != mid){ if(index > mid){ index = paritition(left,index-1); } if(index < mid){ index = paritition(index+1,right); } } //判断链表的长度是奇数还是偶数 if((list.size() & 1) == 1){//奇数 return list.get(index)*1.0; } else{//偶数 return (list.get(mid)+list.get(mid-1))/2.0; } } /** * 快排的分区函数[小,pivot,大] * @param left * @param right * @return */ public static int paritition(int left,int right){ Integer pivot = list.get(left); while(left < right){ while(left<right && list.get(right) >= pivot) right--; list.set(left, list.get(right)); while(left<right && list.get(left) < pivot) left++; list.set(right, list.get(left)); } list.set(right, pivot); return right; } public static void main(String[] args) { for(int i = 1; i < 20; i++){ Insert(i); Double median = GetMedian(); System.out.println(median); } } }
package _52.数据流中的中位数;
import java.util.ArrayList;
/**
* 使用最大堆、最小堆解决
* @author Administrator
*
*/
public class MedianFinder2 {
private static ArrayList<Integer> maxHeap = new ArrayList<>();//大根堆
private static ArrayList<Integer> minHeap = new ArrayList<>();//小根堆
public static void Insert(Integer num) {
//1.如果大根堆为空,直接加入大根堆中
if(maxHeap.size() == 0 || num <= maxHeap.get(0)){
maxHeap.add(num);
for(int i = maxHeap.size()/2 ; i >= 0; i--){
maxShif(i);
}
}
//2.如果大根堆不为空,判断num和大根堆中最大元素的大小,如果num小于大根堆中的最大元素,则加入大根堆
// else if(num < maxHeap.get(0)){
// maxHeap.add(num);
// for(int i = maxHeap.size()/2 ; i >= 0; i--){
// maxShif(i);
// }
// }
//3.如果num大于大根堆中的最大值,看是否能加进小根堆中
//3.1 如果小根堆为空则加入小根堆,
else if(minHeap.isEmpty() || num > minHeap.get(0)){
minHeap.add(num);
for(int i = minHeap.size()/2 ; i >= 0; i--){
minShif(i);
}
System.out.println(minHeap.toString());
}
//3.2 如果num大于小根堆的最小值,则加入小根堆
// else if(num > minHeap.get(0)){
// minHeap.add(num);
// for(int i = minHeap.size()/2 ; i >= 0; i--){
// minShif(i);
// }
// }
//3.3 如果num小于小根堆的最小值,则加入大根堆
else if(num <= minHeap.get(0)){
maxHeap.add(num);
for(int i = maxHeap.size()/2 ; i >= 0; i--){
maxShif(i);
}
System.out.println(maxHeap.toString());
}
//平衡两个堆中的元素
if (minHeap.size() > maxHeap.size() + 1){
maxHeap.add(minHeap.remove(0));
}
else if(maxHeap.size() > minHeap.size() + 1){
minHeap.add(maxHeap.remove(0));
}
for(int i = maxHeap.size()/2 ; i >= 0; i--){
maxShif(i);
}
for(int i = minHeap.size()/2 ; i >= 0; i--){
minShif(i);
}
}
public static Double GetMedian() {
if(maxHeap.isEmpty() && minHeap.isEmpty())
return -1.0;
if(maxHeap.size() == minHeap.size()){
return (maxHeap.get(0) + minHeap.get(0))/2.0;
}
else{
return maxHeap.size() > minHeap.size() ? maxHeap.get(0)*1.0 : minHeap.get(0)*1.0;
}
}
public static void maxShif(int low){
int i = low;
int j = 2*i + 1;
int high = maxHeap.size() - 1;
while(j <= high){
if((j<high) && maxHeap.get(j) < maxHeap.get(j+1))
j++;
if(maxHeap.get(i) < maxHeap.get(j)){
Integer tmp = maxHeap.get(i);
maxHeap.set(i, maxHeap.get(j));
maxHeap.set(j, tmp);
i = j;
j = i*2+1;
}
else break;
}
}
public static void minShif(int low){
int i = low;
int j = 2*i + 1;
int high = minHeap.size() - 1;
while(j <= high){
if((j<high) && minHeap.get(j) > minHeap.get(j+1))
j++;
if((j<high) && minHeap.get(i) > minHeap.get(j)){
Integer tmp = minHeap.get(i);
minHeap.set(i, minHeap.get(j));
minHeap.set(j, tmp);
i = j;
j = 2*i + 1;
}
else break;
}
}
public static void main(String[] args) {
for(int i = 1; i < 20; i++){
Insert(i);
Double median = GetMedian();
System.out.println(median);
}
}
}