解题思路:
第一种方法(使用自定义的Heap类实现)
/**为了保证两边数据量的平衡<ul><li>两边数据一样时,加入左边</li><li>两边数据不一样时,加入右边</li></ul>但是, 随便一个数能直接加入吗?<ul><li>加入左边前, 应该挑右边最小的加入</li><li>加入右边前, 应该挑左边最大的加入</li></ul>*/
题解
//设置左边履行的是大顶堆
private Heap left = new Heap(10, false);
//设置右边履行的是小顶堆
private Heap right = new Heap(10, true);/**为了保证两边数据量的平衡<ul><li>两边数据一样时,加入左边</li><li>两边数据不一样时,加入右边</li></ul>但是, 随便一个数能直接加入吗?<ul><li>加入左边前, 应该挑右边最小的加入</li><li>加入右边前, 应该挑左边最大的加入</li></ul>*/
public void addNum(int num) {if (left.size() == right.size()) {right.offer(num);left.offer(right.poll());} else {left.offer(num);right.offer(left.poll());}
}/*** <ul>* <li>两边数据一致, 左右各取堆顶元素求平均</li>* <li>左边多一个, 取左边元素</li>* </ul>*/
public double findMedian() {if (left.size() == right.size()) {return (left.peek() + right.peek()) / 2.0;} else {return left.peek();}
}
可以扩容的 heap, max 用于指定是大顶堆还是小顶堆:
public class Heap {int[] array;int size;boolean max;public int size() {return size;}//当max为true则为大顶堆 如果是false则为小顶堆public Heap(int capacity, boolean max) {this.array = new int[capacity];this.max = max;}/*** 获取堆顶元素** @return 堆顶元素*/public int peek() {return array[0];}/*** 删除堆顶元素** @return 堆顶元素*/public int poll() {int top = array[0];swap(0, size - 1);size--;down(0);return top;}/*** 删除指定索引处元素** @param index 索引* @return 被删除元素*/public int poll(int index) {int deleted = array[index];swap(index, size - 1);size--;down(index);return deleted;}/*** 替换堆顶元素** @param replaced 新元素*/public void replace(int replaced) {array[0] = replaced;down(0);}/*** 堆的尾部添加元素** @param offered 新元素*/public void offer(int offered) {if (size == array.length) {grow();}up(offered);size++;}//如果容量不够就进行扩容private void grow() {int capacity = size + (size >> 1);int[] newArray = new int[capacity];//将原有的数组重新放到扩容好的数组中System.arraycopy(array, 0,newArray, 0, size);array = newArray;}// 将 offered 元素上浮: 直至 offered 小于父元素或到堆顶private void up(int offered) {int child = size;while (child > 0) {int parent = (child - 1) / 2;boolean cmp = max ? offered > array[parent] : offered < array[parent];if (cmp) {array[child] = array[parent];} else {break;}child = parent;}array[child] = offered;}public Heap(int[] array, boolean max) {this.array = array;this.size = array.length;this.max = max;heapify();}// 建堆private void heapify() {// 如何找到最后这个非叶子节点 size / 2 - 1for (int i = size / 2 - 1; i >= 0; i--) {down(i);}}// 将 parent 索引处的元素下潜: 与两个孩子较大者交换, 直至没孩子或孩子没它大private void down(int parent) {int left = parent * 2 + 1;int right = left + 1;int min = parent;if (left < size && (max ? array[left] > array[min] : array[left] < array[min])) {min = left;}if (right < size && (max ? array[right] > array[min] : array[right] < array[min])) {min = right;}if (min != parent) { // 找到了更大的孩子swap(min, parent);down(min);}}// 交换两个索引处的元素private void swap(int i, int j) {int t = array[i];array[i] = array[j];array[j] = t;}
}
本题还可以使用平衡二叉搜索树求解,不过代码比两个堆复杂
第二种方法(使用java现成的类实现)
上面我们是使用的是自定义的heap类来进行实现的,但是我们Java其实是有这么一个现成的类:“PriorityQueue”
解题思路:
/**
* 为了保证两边数据量的平衡
* <ul>
* <li>两边个数一样时,左边个数加一</li>
* <li>两边个数不一样时,右边个数加一</li>
* </ul>
* 但是, 随便一个数能直接加入吗?
* <ul>
* <li>左边个数加一时, 把新元素加在右边,弹出右边最小的加入左边</li>
* <li>右边个数加一时, 把新元素加在左边,弹出左边最小的加入右边</li>
* </ul>
*/
代码:
public class E04Leetcode295_2 {/*** 为了保证两边数据量的平衡* <ul>* <li>两边个数一样时,左边个数加一</li>* <li>两边个数不一样时,右边个数加一</li>* </ul>* 但是, 随便一个数能直接加入吗?* <ul>* <li>左边个数加一时, 把新元素加在右边,弹出右边最小的加入左边</li>* <li>右边个数加一时, 把新元素加在左边,弹出左边最小的加入右边</li>* </ul>*/public void addNum(int num) {if (left.size() == right.size()) {right.offer(num);left.offer(right.poll());} else {left.offer(num);right.offer(left.poll());}}/*** <ul>* <li>两边数据一致, 左右各取堆顶元素求平均</li>* <li>左边多一个, 取左边堆顶元素</li>* </ul>*/public double findMedian() {if (left.size() == right.size()) {return (left.peek() + right.peek()) / 2.0;} else {return left.peek();}}// 大顶堆private PriorityQueue<Integer> left = new PriorityQueue<>((a, b) -> Integer.compare(b, a) //);// 小顶堆private PriorityQueue<Integer> right = new PriorityQueue<>((a, b) -> Integer.compare(a, b) //);public static void main(String[] args) {/*E04Leetcode295_2 test = new E04Leetcode295_2();test.addNum(1);test.addNum(2);test.addNum(3);test.addNum(7);test.addNum(8);test.addNum(9);System.out.println(test.findMedian());test.addNum(10);System.out.println(test.findMedian());test.addNum(4);System.out.println(test.findMedian());*/// 以上浮为例, 大概的实现逻辑
// Comparator<Integer> cmp = (a, b) -> Integer.compare(a, b); // 小顶堆比较器 compare -1 a<b, 0 a==b, 1 a>bComparator<Integer> cmp = (a, b) -> Integer.compare(b, a); // 大顶堆比较器 compare -1 b<a, 0 b==a, 1 b>aint a = 10; // 父元素值int b = 20; // 新增元素值if (cmp.compare(a, b) > 0) {System.out.println("上浮");} else {System.out.println("停止上浮");}}}