Q64:数据流中的中位数

原创 2016年08月30日 20:17:18
import java.util.ArrayList;
import java.util.Comparator;
import org.omg.CORBA.PUBLIC_MEMBER;
public class Q64数据流中的中位数 {
	/**
	 * 题目:数据流中的中位数
	 * 题目说明:如何得到一个数据流的中位数?如果从数据流中读取奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
	 *       如果从数据流中读取偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
	 * 解题思路:下面的代码主要描述了堆的相关操作,大顶堆和小顶堆。
	 */
	public static void main(String[] args) {
		DynamicArray array = new DynamicArray();
		BinaryTreeNode root = new BinaryTreeNode();
		root.value = 5;
        array.insert(root);
        System.out.println(array.getMedian());
	}
	private static class Heap<BinaryTreeNode>{
		private ArrayList<BinaryTreeNode> data;//堆中元素存放的集合
		private Comparator<BinaryTreeNode> cmp;//比较器
		
		public Heap(Comparator cmp){//构造函数比较器对象
			this.cmp = cmp;
			this.data = new ArrayList<BinaryTreeNode>(64);
		}
		
		public void shiftUp(int index){//向上调整,其中index是被移动的元素的起始位置
			if(index < 0 || index >= data.size()){
				throw new IllegalArgumentException(index + "");
			}
			
			BinaryTreeNode intent = data.get(index);//获取开始调整的元素对象
			
			while(index > 0){
				int indexParent = (index - 1) / 2;//找到要调整元素的父结点
				BinaryTreeNode parent = data.get(indexParent);//父结点对象值
				//比较当前结点和父结点的大小,如果当前结点大于父结点的对象值,则向上移动
				if(cmp.compare(intent, parent) > 0){
					//父结点下移,并记录其下移后的位置
					data.set(index, parent);
					index = indexParent;
				}else {//当前结点小于父结点,不移动元素
					break;
				}
			}
			//此处index记录的是最后一个被下放的父结点的位置,因此将最开始调整的元素值value放入到index的位置即可
			data.set(index, intent);
		}
		
		public void shiftDown(int index){//向下调整,其中index是被移动的元素的起始位置
			//合法性判断,检查位置是否正确
			if(index < 0 || index >= data.size()){
				throw new IllegalArgumentException(index + "");
			}
			
			BinaryTreeNode intent = data.get(index);//获取调整元素的对象
			int leftIndex = 2 * index +1;
			while(leftIndex < data.size()){//左子节点存在
				//取左子节点的元素对象,假设左子节点为两个子结点的最大致
				BinaryTreeNode maxChild = data.get(leftIndex);
				//子结点中最大元素的位置
				int maxIndex = leftIndex;
				
				int rightIndex = leftIndex + 1;//右结点的位置
				if(rightIndex < data.size()){//如果右结点存在
					BinaryTreeNode rightChild = data.get(rightIndex);
					//比较左右子结点的大小,将较大的元素存入到maxChild 中
					if(cmp.compare(rightChild, maxChild) > 0){
						maxChild = rightChild;
						maxIndex = rightIndex;
					}
				}
				
				if(cmp.compare(maxChild, intent) > 0){//子结点中较大的元素比父结点的值要大
					//将较大的结点向上移动
					data.set(index, maxChild);
					//记录上移结点的位置
					index = maxIndex;
					//上移结点的左子节点的位置
					leftIndex = 2 * index + 1;
				}else {//最大节点不必父结点大,说明父子结点已经是从大到小排好序了
					break;
				}
			}
			//index记录的是最后一个被上移的子结点,将最开始调整的元素存入到index的位置即可
			data.set(index, intent);
		}
		
		public void add(BinaryTreeNode item){
			data.add(item);//添加到最后
			//添加完后,上移,完成重构
			shiftUp(data.size() - 1);
		}
		
		public BinaryTreeNode deleteTop(){
			if(data.isEmpty())
				throw new RuntimeException("堆是空的!");
			
			//获取堆顶元素
			BinaryTreeNode first = data.get(0);
			//删除最后一个元素 
			BinaryTreeNode last = data.remove(data.size() - 1);
			//删除元素可能是堆顶元素且为最后一个元素
			if(data.size() == 0){
				return last;
			}else {
				//将删除的元素放入堆顶
				data.set(0, last);
				shiftDown(0);//自上向下调整堆
				return first;//返回堆顶元素
			}
		}
		
		public BinaryTreeNode getTop(){//获取堆顶元素,但不删除
			//如果堆已经为空,则抛出异常
			if(data.isEmpty()){
				throw new RuntimeException("堆已经为空!");
			}
			return data.get(0);
		}
		
		public int size(){//获取堆的大小
			return data.size();
		}
		
		public boolean isEmpty(){//判断堆是否为空
			return data.isEmpty();
		}
		
		public void clear(){//清空堆
			data.clear();
		}
		
		public ArrayList<BinaryTreeNode> getData(){//获取堆中所有的数据
			return data;
		}
	}
	
	//升序比较器
	private static class InoComparator implements Comparator<Integer> {
		@Override
		public int compare(Integer o1, Integer o2){
			return o1 - o2;
		}
	}
	
	//降序比较器
	private static class DescComparator implements Comparator<Integer>{
		@Override
		public int compare(Integer o1, Integer o2){
			return o2 - o1;
		}
	}
	
	//数据流
	private static class DynamicArray{
		private Heap<BinaryTreeNode> max;
		private Heap<BinaryTreeNode> min;
		
		public DynamicArray(){
			max = new Heap<BinaryTreeNode>(new InoComparator());//大顶堆,存放小数的
			min = new Heap<BinaryTreeNode>(new DescComparator());//小顶堆,存放大数的
		}
		
		//插入数据
		public void insert(BinaryTreeNode node){
			//先判断堆中已经有的数据是不是偶数个,如果是偶数个,则将node添加到小顶堆中
			if((min.size() + max.size()) % 2 == 0){
				//大堆中有数据,并且插入的元素比大堆中的元素小,则添加到大堆中
				if(max.size() > 0 && node.value < max.getTop().value){
					max.add(node);
					//删除堆顶元素,大堆中的最大元素
					node = max.deleteTop();
				}
				min.add(node);
			}else {//两堆中的总数是奇数时,将数据插入到大堆中
				//小堆中有数据,并且插入的元素比小堆中的元素大
				if(min.size() > 0 && node.value > min.getTop().value){
					min.add(node);
					node = min.deleteTop();
				}
				max.add(node);
				
			}
		}
		
		public double getMedian(){//获取中位数
			int size = max.size() + min.size();
			if(size == 0){
				throw new RuntimeException("没有元素");
			}
			if((size & 1) == 1){//奇数个元素
				return min.getTop().value;
			}else {
				return (max.getTop().value + min.getTop().value) / 2.0;
			}
			
		}
	}
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

64 - 数据流中的中位数 || STL 堆

题目: 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。思路...

剑指offer面试题64 数据流中的中位数(Java实现)

解题思路:具体见注释 import java.util.Comparator; import java.util.PriorityQueue; public class Solution { ...

LeetCode 295. Find Median from Data Stream(数据流中位数)

原题网址:https://leetcode.com/problems/find-median-from-data-stream/ Median is the middle value in an ...
  • jmspan
  • jmspan
  • 2016年04月16日 11:09
  • 394

数据流中的中位数(Java实现)

本题为剑指offer 面试题64 牛客网测试地址:https://www.nowcoder.com/questionTerminal/9be0172896bd43948f8a32fb954e1be1...

牛客网刷题笔记--剑指offer(数据流中的中位数)

题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 ...

Q25_数据流中的中位数

题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值...

《剑指offer》:[64]数据流中的中位数

题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数据排序后中间两个数的平均值。 例如:...
  • gogoky
  • gogoky
  • 2016年06月30日 10:21
  • 1150

【剑指Offer】面试题64:数据流中的中位数

整理自剑指Offer 牛客网https://www.nowcoder.com/questionTerminal/9be0172896bd43948f8a32fb954e1be1 一:题目描...

剑指offer-面试题64-数据流中的中位数

package case64_StreamMedian; import java.util.Comparator; import java.util.PriorityQueue; /** * 题...

剑指offer 面试题64 数据流中的中位数

剑指offer 面试题64 数据流中的中位数 题目: 如何得到一个数据流中的中位数? 如果从数据流中读出期数个数值,那么中位数就是所有数值排序之后位于中间的数值。 如果从数据...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Q64:数据流中的中位数
举报原因:
原因补充:

(最多只允许输入30个字)