随时获取数据流的中位数

题目

有一个源源不断往外吐出整数的数据流,假设你有足够的空间来保存吐出的数。请设计一个方法,这个方法可以随时取出之前吐出所有数的中位数

要求
  1. 如果已经保存了吐出的N个数,那么任意时刻将一个新数加入的过程,其时间复杂度不超过O(logN)
  2. 取得中位数的过程,时间复杂度为O(1)
思路

建立一个大根堆,一个小根堆。每次来的一个数,和大根堆的堆顶比较,如果小于大根堆的堆顶,就加入大根堆;如果大于大根堆的堆顶,就加入小根堆

同时还要满足这两个堆中的元素个数之差不能超过2(即<2)。例如大根堆中的元素现在有3个,小根堆中的元素有1个,此时就需要把大根堆的堆顶弹出,放入小根堆中;反之也一样。注意:每次往堆中加入数的同时,也要调整堆的结构

如果吐出的数据个数为偶数,则中位数是两个堆的堆顶相加除以2;为奇数,中位数是元素个数较多的那个堆的堆顶

往堆里加入一个数的时间复杂度是O(logN),取出中位数的时间复杂度是O(1),满足题目要求

代码
import java.util.Comparator;
import java.util.PriorityQueue;

public class GetMedian {

	public static class MinHeapComparator implements Comparator<Integer> {

		@Override
		public int compare(Integer a, Integer b) {
			return a > b ? 1 : -1;
		}
	}

	public static class MaxHeapComparator implements Comparator<Integer> {

		@Override
		public int compare(Integer a, Integer b) {
			return b > a ? 1 : -1;
		}
	}

	public static void main(String[] args) {
		int[] arr = { 8, 3, 4, 6, 9, 7, 1 }; // 1 3 4 6 7 8 9
		double median = getMedian(arr);
		System.out.println(median);
	}

	private static double getMedian(int[] arr) {
		PriorityQueue<Integer> big = new PriorityQueue<Integer>(new MaxHeapComparator());
		PriorityQueue<Integer> small = new PriorityQueue<Integer>(new MinHeapComparator());
		for (int i = 0; i < arr.length; i++) {
			int cur = arr[i];
			if (big.isEmpty() || cur < big.peek())
				big.add(cur);
			else
				small.add(cur);
			if (big.size() - small.size() >= 2)
				small.add(big.poll());
			else if (small.size() - big.size() >= 2)
				big.add(small.poll());
		}
		if (arr.length % 2 == 0)
			return ((double) (big.peek() + small.peek()) / 2);
		else
			return big.size() > small.size() ? big.peek() : small.peek();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
计算数据流中位数可以通过Flink的ProcessFunction来实现。 具体实现步骤如下: 1. 将数据流按照大小排序 2. 计算数据流的长度,如果是奇数,则中位数为第 (length+1)/2 个元素;如果是偶数,则中位数为第length/2个元素和第(length/2+1)个元素的平均值。 3. 在ProcessFunction的实现中,可以使用状态变量来保存数据流的有序列表,并计算中位数。 以下是一个简单的示例代码: ```java public class MedianFunction extends ProcessFunction<Integer, Double> { private ListState<Integer> values; @Override public void open(Configuration parameters) throws Exception { super.open(parameters); values = getRuntimeContext().getListState(new ListStateDescriptor<Integer>("values", Integer.class)); } @Override public void processElement(Integer value, Context ctx, Collector<Double> out) throws Exception { values.add(value); List<Integer> sortedValues = new ArrayList<>(); for (Integer v : values.get()) { sortedValues.add(v); } Collections.sort(sortedValues); int length = sortedValues.size(); if (length % 2 == 0) { double median = (sortedValues.get(length/2) + sortedValues.get(length/2 - 1)) / 2.0; out.collect(median); } else { double median = sortedValues.get(length/2); out.collect(median); } } } ``` 在上述代码中,我们使用了ListState来保存数据流中的元素,并在每次处理新元素时重新排序并计算中位数。注意,这只是一个简单的示例,实际应用中需要考虑更多的问题,比如数据倾斜、数据丢失等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数学家是我理想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值