Heap in Java
PriorityQueue
操作queue的interface,offer() O(log), peek() O(1), poll() O(logn), size(), isEmpty(); 不再是FIFO,而是按照优先级的out。最高层次的抽象,使用的角度,用它的语义层面它是queue;落实的层面,array。
remove() O(logn) - 堆顶的pop,这是throw exception的
remove(Object) O(n) - remove特定的node,要search,最坏情况每个都要看,不能看到某个node就马上剪枝,因为左右的大小关系不知道。和BST不同,BST中的删除是O(height)
why array: 其实当然也可以用binary tree,但是这样做的存储效率非常低,locality和overhead的问题。更重要的原因是给一组数据来做heapify的时候,拿到的东西都是array,本身拿到的就是一个连续存放的array。此时,“树在我心中”。为什么是complete binary tree? 想想array里的数据也是连续存的。如此,array的每一个数都和binary tree有了一一映射。树长什么样是不会随内容的变化而变化的。
Order
如何定义优先级?
Comparable:Element-type implementing comparable interface
"可比较的",自己和别人比,方法是compareTo,objectA.compareTo(objectB)
默认方法创建的装integer的heap是min heap 其实是因为默认生成的是“优先级最高”的堆。
PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
//值一样返回0 说明优先级一样
//<0说明当前元素的优先级比传入的another更高。
Integer中有compareTo这一段代码,所以序号小,优先级高,返回小于0的数。通过返回值的=<>,判断priorityQueue的优先级相对大小。
Comparator: Provide an extra comparator object to compare the elements
“用于比较的工具” 可以认为是第三方,它拿来两个来比,方法是compare,
myComparator.compare(objectA, objectB)
interface Comparator<E> {
int compare (E o1, E o2);
}
class Cell {
public int row;
public int col;
public int value;
public Cell (int row, int col, int value) {
this.row = row;
this.col = col;
this.value = value;
}
}
class MyComparator implements Comparator<Cell> {
@override
public int compare(Cell c1, Cell c2) {
if (c1.value==c2.value) {
return 0;
}
return c1.value < c2.value ? -1; 1;
}
}
这里不要用c1.value-c2.value,因为这会有溢出的问题,不要!比如,c1是特别小的负数,c2是特别大的正数。
PriorityQueue<Cell> minHeap = new PriorityQueue<Cell>(11, new MyComparator());
如果已经有一个comparable(装哪个类型,这个类型提供一个comparable),但是想用一个新的定义优先级的方法,那就新写一个comparator.
class ReverseComparator implements Comparator<Integer> {
@override
public int compare(Integer i1, Integer i2) {
if (i1.equals(i2)) {
return 0;
}
return i1 < i2 ? 1 : -1;
}
}
comparable只能最多定义出来一种顺序,与生俱来的顺序,叫natural ordering,自然序。comparator是第三方的比较器,create
小技巧
如果想要的是自然序的反序,Collections.reverseOrder()
, 创建了自然序反序的comparator,不需要自己创建一个comparator了。static method, 不需要任何instance就可以调用,时间复杂度O(1)。
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer> (16, collections.reverseOrder());
Possible ways to provide
顶格写 Top-level class
写到class里 Static Nested class 活在类里面的类
匿名类 Anonymous class
Lambda Expressions

PriorityQueue Constructor
1.PriorityQueue<Cell> heap = new PriorityQueue<Cell>();
initialize the internal array with default capacity (11)
class Cell implements Comparable<ell>!
2. PriorityQueue<Cell> heap = new PriorityQueue<Cell>(16);
initialize the internal array with specified capacity (16)
class Cell implements Comparable<cell>! natural ordering 所以假如调用了一个没有comparable的时候,会在运行的比较时抛异常 而且是在Java自己的源代码里抛出异常 cast exception
3. PriorityQueue<Cell> heap = new PriorityQueue<Cell>(16, new MyComparator());
initialize the internal array with specified capacity (16)
class MyComparator implements Comparable<Cell>!
4. PriorityQueue<Cell> heap = new PriorityQueue<Cell>(new MyComparator());
class MyComparator implements Comparator <Cell>
Java 8+ only
Nested Class
Java里允许类里面嵌套类
Anonymous Class 匿名类 在方法里只在这里用这个类,只用这一次,不会重用,不存在复用,所以名字都没起,类中路人甲。在priority queue中路人甲很常见,因为我们需要给与comparator,这个顺序可能只在这个priorityqueue中用一次,也不会再被使用了。

PriorityQueue<Cell> pQueue = new PriorityQueue<>
1) 实现一个接口,comparator
2)定义一个类
3)创建一个instance
4)Call Constructor
最外面的()是constructor的 以上相当于
实现heap
public class MinHeap {
private int[] array;
private int size;
public MinHeap(int[] array) {
if (array==null||array.length==0) {
throw new illegalArgumentException("input array cannot e null or empty");
}
this.array = array;
size = array.length;
heapify();
}
//see below
}
PercolateUp -- offer/update
老板家儿子公司体验生活
compare with the parent, swap until parent has higher priority than self
private void percolateUp(int index) {
int parent_idx = (index-1)/2;
if (parent_idx < 0 or array[index] > array[parent_idx]) {
return;
}
swap(index, parent_idx);
array[index], array[parent_idx] = array[parent_idx], array[index];
percolateUp(array, parent_idx)
}
private void percolateUp(int index) {
while(index>0) { //index<=0 exit
int parent_idx = (index-1)/2;
if (array[parent_idx])>array[index] {
swap(array, parent_idx, index);
} else {
break;
}
index = parent_idx
}
}
PercolateDown --poll/ update/ heapify
皇帝驾崩 percolate down的条件是只有堆顶没了,左右children都是好的;此时做percolate down,就能很好的维护heap property
swap
percolate down, compare with both children, swap with the smaller one
//parent要和children交换,children可能有null 技巧是swapcandidate 默认和左边比,因为可能没有右边
//只有右边有子女且右边比左边小时才swap右边 而且要用长度判断,不能直接== 因为这个时候已经out of bound了
private void percolateDown(int index) {
}
Heapify()
正经的heapify O(n)
一个节点的时候就是一个heap,所以可以一个个做percolate down,让以新元素为parent的subtree变成一个更大的heap。
从底到上,从右往左的顺序,对非叶节点做percolate down,一路往回走 两个视角,array的视角,保证代码简单;tree的视角维护heap的性质。
如何找“最后一个非叶节点”? (最后一个元素index-1)/2一定是parent = (n-2)/2=n/2-1 其中n是array length
时间复杂度O(n)
总共k层,1个元素向下看k-1层,2个元素向下看k-2层...

错位相减,k=logn
private void heapify() {
for (int i=size/2-1;i>-1;i--) {
percolateDown(i);
}
}
假heapify: 把n个元素插入进heap O(nlogn)
offer and percolate up

Update()
看变大还是变小,调整update的元素以保持heap property
Poll()
把size当一个挡板,换完后挡板往前移 (size--)
Offer()
Java的API vs 自己实现
Java的API无限,自己实现的有size,如果想要很长的,那array list扩容,*1.5挪了
如果想自己实现一个支持comparator的priority queue,改所有update比大小的地方换成compare(a,b)的方法
Smallest K Elements in Unsorted Array
put all the elements in a min heap, pop k times 复杂度
put k elements in a max heap, insert (n-k) times
public static int[] smallestKElements(int[] array, int k) {
Queue<Integer> minHeap = new PriorityQueue<>;
for (int i = 0; i < array.length; i ++) {
minHeap.offer(array[i]);
}
int [] result = new int[k];
for (int i = 0; i< k; i++) {
result[i] = minHeap.poll();
}
return result;
}
stream processing vs batch processing
Sorting Algorithms (and heap)
Java的arrays.sort来sort int[] array, 会使用quicksort;如果使用的是Integer array, 会使用mergesort。
quicksort 对primitive type的排序
time worst case O(n^2), average O(nlogn), space worst case O(n), average O(logn)
unstable sort 排完序之后的顺序可能会改变 但对于primitive type来说,但这无所谓
mergesort 对object的排序
time worst case O(nlogn), average O(nlogn), space worst/average case O(n)
stable sort key一样时不会改变两个元素的先后顺序 对于有多个field的object非常重要
比如,如果在Excel sheet里,先按照从高到低的分数排序,再按照性别排序,那么每个性别内部排序也是对的
heapsort space O(1), time O(nlogn)
selection sort虽然可以 in place,但是每次往后看选出当前待排序的元素的最小的,都要完整的扫一遍;如果能把后面的都做成heap,那么这个时候拿出来一个元素再对heap做调整的空间复杂度是logn。
用heap来加速的selection sort:heap sort
把待排序的放在左边的max heap里,把堆顶元素往最后swap,堆越来越小,已排序的越来越大;每次拿出一个元素是logn,总共n个元素,所以时间复杂度是nlogn; 空间上 in place.
工业界不常用 原因
虽然看起来复杂度低,但是操作的时间,heapify的时间长;
算法locality比较差,因为头部节点扔到尾部,long time overhead;
heap sort无法distribute/parallelize 执行人唯一
Last updated