【剑指offer】41. 数据流中的中位数

题目描述

在这里插入图片描述

在这里插入图片描述

// 41. 数据流中的中位数

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

// 例如,
// [2,3,4] 的中位数是 3
// [2,3] 的中位数是 (2 + 3) / 2 = 2.5
// 设计一个支持以下两种操作的数据结构:

// void addNum(int num) - 从数据流中添加一个整数到数据结构中。
// double findMedian() - 返回目前所有元素的中位数。



// 牛客
// 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么
// 中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数
// 个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使
// 用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中
// 位数。

题解

/ 双堆维护 /
// 数据流要么有奇数个,要么有偶数个。
// 如果监控存入的元素个数size,必然size的变化有:奇数->偶数->奇数->偶数...
// 我们将第奇数个和第偶数个流入的数据分开,并使用最大堆最小堆来保存。

// 这是由于最大堆可以自动维护较小值(通过弹出最大值),最小堆可以自动维护较大值(通过弹出最小值。
// 由于第奇数个数先于第偶数个数存入,所以第奇数个数存入堆A(最大堆
// 还是最小堆无所谓),那么第偶数个数就必须存入堆B(是什么堆无所谓,反正跟A不同就行)
// 这个解里我令第1个,第3个...,这些第奇数个流入的数组存入最小堆,
// 令第2个,第4个...,第偶数个流入的数字存入最大堆。
// 数据在入堆之前还必须经过相反堆的筛选,比如第1个存入的数字规定存入最小堆
// 那么先进入最大堆进行筛选,此时最大堆弹出堆顶值(最大值),再存入最小堆,
// 第2个存入的数字同理,以此类推。
// 则最大堆会遍历数据流里排名奇数位的数字,并弹出的所有大值给最小堆
// 最小堆也会遍历数据流也排名偶数位的数字,并弹出所有的小值给最大堆。
// 经过这样的筛选,所有的大值都会给最小堆,所有的小值都会给最大堆。
// 求中位数时,只需看堆顶元素和数据流个数即可。

// 如果数据总数有偶数个,那么取大值里的最小值(最小堆堆顶)和
// 小值里的最大值(最大堆堆顶)求和÷2即可得到中位数。
// 如果数据总数为奇数个,取第奇数个存入堆(是最大堆还是最小堆无所谓)
// 的堆顶元素,就是中位数。


// 牛客
// 此解设第奇数个数字的存储堆为最小堆,第偶数个数据的存储堆为最大堆
// 运行时间:16ms
// 占用内存:10044k
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
	// 最大堆maxHeap:用于筛选大值传入最小堆minHeap,自身用于保存小值。
	private PriorityQueue<Integer> maxHeap = new PriorityQueue<>( new Comparator<Integer>() {
		@Override
		public int compare(Integer o1, Integer o2) {
			return o2.compareTo(o1);
		}
	});
	// 最小堆minHeap:用于筛选小值传入最大堆maxHeap,自身用于保存大值。
	private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
	private int size = 0;  // size让我们监控所有元素的总数

    public void Insert(Integer num) {
		// 如果已有偶数个元素,则将第奇数个元素num,存入最大堆中
		// ,筛选出最大堆的最大值弹出,存入最小堆中。
		// 如果当前数num在最大堆maxHeap就是最大值,会被直接存入
		// 最小堆minHeap,如果在maxHeap中不是最大值,那么会maxHeap
		// 会自己推出一个堆内当前的最大值给minHeap,相当于做了数据交换。
		if (size % 2 == 0) {
			maxHeap.add(num);
			minHeap.add(maxHeap.poll());
		} 
		else {  // 如果已有奇数个元素,则将第偶数个元素存入最小堆中
			minHeap.add(num);  
			// 后将最小堆中最小值弹出(筛选),存入最大堆中。
			maxHeap.add(minHeap.poll());
		}
		size++;  // 每存入一个数据,size+1,记录元素的总数
    }

    public Double GetMedian() {
		// 两个堆元素相同,元素个数为偶数
		if (maxHeap.size() == minHeap.size()) 
			return (double) (maxHeap.peek() + minHeap.peek()) / 2.0;
		else 
			return (double) minHeap.peek();
	}
}



// 牛客
// 此解设第奇数个数字的存储堆为最大堆,第偶数个数据的存储堆为最小堆
// 运行时间 18ms
// 占用内存 10180KB
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {

	private PriorityQueue<Integer> maxHeap = new PriorityQueue<>( new Comparator<Integer>() {
		@Override
		public int compare(Integer o1, Integer o2) {
			return o2.compareTo(o1);
		}
	});
	private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
	private int size = 0;

    public void Insert(Integer num) {
		if (size % 2 == 0) {
			minHeap.add(num);
			maxHeap.add(minHeap.poll());
		}
		else {
            maxHeap.add(num);  
			minHeap.add(maxHeap.poll());
		}
		size++;
    }

    public Double GetMedian() {
		if (maxHeap.size() == minHeap.size()) 
			return (double) (maxHeap.peek() + minHeap.peek()) / 2.0;
		else 
			return (double) maxHeap.peek();
	}
}






// 力扣
// 此解设第奇数个数字的存储堆为最小堆,第偶数个数据的存储堆为最大堆
// 执行用时:88 ms, 在所有 Java 提交中击败了34.84%的用户
// 内存消耗:49.4 MB, 在所有 Java 提交中击败了84.64%的用户
import java.util.PriorityQueue;
import java.util.Comparator;
class MedianFinder {
    private PriorityQueue<Integer> maxHeap, minHeap;
    int size;

    public MedianFinder() {
        maxHeap = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        minHeap = new PriorityQueue<>();
        size = 0;
    }
    
    public void addNum(int num) {
        if (size % 2 == 0) {
            maxHeap.add(num);
            minHeap.add(maxHeap.poll());
        }
        else {
            minHeap.add(num);
            maxHeap.add(minHeap.poll());
        }
        size++;
    }
    
    public double findMedian() {
        if (minHeap.size() == maxHeap.size())
            return (double) (minHeap.peek() + maxHeap.peek()) / 2.0;
        else 
            return (double) minHeap.peek();
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */



// 力扣
// 此解设第奇数个数字的存储堆为最大堆,第偶数个数据的存储堆为最小堆
// 执行用时:92 ms, 在所有 Java 提交中击败了26.29%的用户
// 内存消耗:49.7 MB, 在所有 Java 提交中击败了43.06%的用户
import java.util.PriorityQueue;
import java.util.Comparator;
class MedianFinder {
    private PriorityQueue<Integer> maxHeap, minHeap;
    int size;

    public MedianFinder() {
        maxHeap = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        minHeap = new PriorityQueue<>();
        size = 0;
    }
    
    public void addNum(int num) {
        if (size % 2 == 0) {

            minHeap.add(num);
            maxHeap.add(minHeap.poll());
        }
        else {
            maxHeap.add(num);
            minHeap.add(maxHeap.poll());
        }
        size++;
    }
    
    public double findMedian() {
        if (minHeap.size() == maxHeap.size())
            return (double) (minHeap.peek() + maxHeap.peek()) / 2.0;
        else 
            return (double) maxHeap.peek();
    }
}

猜你喜欢

转载自blog.csdn.net/fisherish/article/details/113118967
今日推荐