二叉堆 Binary Heap
Binary Heap的基本概念
- Heap是一个完全二叉树 (Complete Tree)
- 根节点最大是Max Heap, 根节点最小是Min Heap
- Max Heap: Heap的每个节点都大于等于它的子节点
- Min Heap: Heap的每个节点都小于等于它的子节点
- Heap一般通过数组实现。
- 节点N(N是下标)的两个子节点的下标分别为 2N+1 和 2N+2
- 节点N(N是下标)的父节点的下标为
如果 N%2 == 0,父节点为 (N-2)/2
如果 N%2 == 0,父节点为(N-1)/2
数组: 9 8 7 6 5 4 3 2 1
数组: 1 2 3 4 5 6 7 8 9
Binary Heap的核心原理
siftUp
- 对于任意节点,不断的和父节点比较,如果小于父节点则交换(MinHeap),直至不能继续上浮
siftUp代码
protected void siftUp(int i) {
//如果输入Index超出范围,返回0
if(i <= 0) {
return;
}
//找出输入节点的父节点
int parentIndex = (i % 2 == 0) ? (i - 2) / 2: (i - 1) / 2;
//和父节点比较
if(compare(parentIndex,i)) {
swap(parentIndex, i);
//继续上浮
siftUp(parentIndex);
}
else {
return;
}
}
siftDown
- 对于任意节点,不断的和子节点中较大的比较(MaxHeap),如果小于此节点则交换(MinHeap),直至不能继续下沉
protected void siftDown(int i) {
//两个子节点全部越界
if (i* 2 + 2 >= size && i*2 +1 >= size) {
return;
}
int targetIndex = 0;
//如果2N+1没有越界但是2N+2越界
//则子节点为2N+1
if(2*i+2 >= size) {
targetIndex = 2*i+1;
}
else {
//比较两个子节点的值,选大的一个
targetIndex = (compare(2*i+1, 2*i+2)) ? 2*i+2 : 2*i+1;
}
//两个节点比较
if(compare(i, targetIndex)) {
swap(i, targetIndex);
//继续下沉
siftDown(targetIndex);
}
else {
return;
}
}
Binary Heap的基本方法
Heapify
- 将一个数组转换为heap
- 方法是对所有的 Inner Node 进行siftDown操作,从最后一个InnerNode开始
- 最后一个 Inner Node的坐标为 size/2 - 1
如下图,所有的红点为Inner Node.
其中最后一个Inner Node (Node 4)的坐标为 9/2 - 1 = 3
执行下沉的顺序为 :4 3 2 1
数组: 1 2 3 4 5 6 7 8 9
Heapify 代码
public void heapify() {
for(int i = size/2-1; i >= 0; i--){
siftDown(i);
}
}
Push
- 在heap中加入一个新值
- 将新值放在数组的末尾(完全二叉树的末尾)然后执行shiftUp操作
- 不用shiftDown完成push方法是因为不能把根替换成新元素,否则就破坏了树的结构
public void push(Object val) {
checkCapacity();
//放置末尾
heapArray[size] = val;
siftUp(size);
size++;
}
Pop
- 将heap中的根元素(数组的第一个元素)pop出来
- 方法:
- 将根元素和heap的最后一个元素进行交换
- 将此时的最后一个元素(也就是之前的根元素)置空
- 对此时的根元素进行shiftDown操作
对下面的heap进行 Pop()操作
public Object pop() {
if(!isEmpty()){
//首尾交换位置
swap(0, size-1);
Object temp = heapArray[size-1];
//将最好一个置为null
heapArray[size-1] = null;
size--;
//对第一个元素siftDown
siftDown(0);
return temp;
}
System.out.println("Empty Heap");
return null;
}
Binary Heap的时间复杂度
时间复杂度 | 解释 | |
---|---|---|
heapify | O(N) | 每一次shiftdown的时间复杂度是和当前节点的高度相关的,把最小堆看做一棵二叉树,叶子节点一层的下移最多是0,第二层的下移时间最多是1,第三层是2,依此类推;而每一层的最多节点数,对于一棵满二叉树来说,第k层的节点数是2^(h-k),h是二叉树的高度;所以堆化的最大时间复杂度就是Sum((k-1) * 2^(h-k)), k from 1 to h,计算之后得到的是2^h-h-1,也就是n-log(n)-1 = O(n) |
push | O(logn) | 最坏情况就是一直上浮到根元素,复杂度是树的层数,也就是logN |
pop | O(logn) | 最坏情况就是交换完之后,根元素一直siftDown到最后一层,复杂度是树的层数,也就是logN |
Binary Heap的实现
Heap Interface
package heap;
public interface HeapInterface {
public void push(Object val);
public void heapify();
public Object pop();
public Object peek();
}
Heap Abstract
package heap;
import java.util.Iterator;
public abstract class HeapAbstract implements HeapInterface {
public int size;
protected int capacity;
protected Object [] heapArray;
public abstract boolean compare(int j, int i);
//Public Methods
@Override
public void push(Object val) {
checkCapacity();
heapArray[size] = val;
siftUp(size);
size++;
}
@Override
public Object pop() {
if(!isEmpty()){
swap(0, size-1);
Object temp = heapArray[size-1];
heapArray[size-1] = null;
size--;
siftDown(0);
return temp;
}
System.out.println("Empty Heap");
return null;
}
@Override
public Object peek() {
if(!isEmpty()) {
return heapArray[0];
}
return null;
}
//Protected Methods
public void heapify() {
for(int i = size/2-1; i >= 0; i--){
siftDown(i);
}
}
protected void siftDown(int i) {
if (i* 2 + 2 >= size && i*2 +1 >= size) {
return;
}
int targetIndex = 0;
if(2*i+2 >= size) {
targetIndex = 2*i+1;
}
else {
targetIndex = (compare(2*i+1, 2*i+2)) ? 2*i+2 : 2*i+1;
}
if(compare(i, targetIndex)) {
swap(i, targetIndex);
siftDown(targetIndex);
}
else {
return;
}
}
protected void siftUp(int i) {
if(i <= 0) {
return;
}
int parentIndex = (i % 2 == 0) ? (i - 2) / 2: (i - 1) / 2;
if(compare(parentIndex,i)) {
swap(parentIndex, i);
siftUp(parentIndex);
}
else {
return;
}
}
protected void checkCapacity() {
if(size < capacity){
return;
}
Object [] temp = new Object[capacity];
for(int i =0; i < capacity; i++){
temp[i] = heapArray[i];
}
heapArray = new Object[capacity *2];
for(int j=0; j < capacity; j++){
heapArray[j] = temp[j];
}
capacity = capacity *2;
}
protected void swap(int a, int b) {
Object temp = heapArray[a];
heapArray[a] = heapArray[b];
heapArray[b] = temp;
}
protected void display() {
for(int i = 0; i < size; i++) {
System.out.print( " " + heapArray[i]);
}
System.out.println();
}
protected boolean isEmpty() {
if(size == 0) {
return true;
}
return false;
}
protected int size() {
return size;
}
}
Max Heap
package heap;
import java.util.Iterator;
public class MaxHeap extends HeapAbstract {
public MaxHeap() {
capacity = 10;
heapArray = new Object[capacity];
size = 0;
}
public MaxHeap(Object [] input) {
heapArray = input;
capacity = input.length;
size = input.length;
heapify();
}
public MaxHeap(int length) {
capacity = length;
heapArray = new Object[size];
size = 0;
}
@Override
public boolean compare(int j, int i ) {
if((Integer)heapArray[j] < (Integer)heapArray[i]) {
return true;
}
return false;
}
public static void main(String[] args) {
Object [] array = {1,2,3,4,5,6};
MaxHeap myHeap = new MaxHeap(array);
myHeap.display();
}
}
Min Heap
package heap;
import java.util.Iterator;
public class MinHeap extends HeapAbstract {
public MinHeap() {
capacity = 10;
heapArray = new Object[capacity];
size = 0;
}
public MinHeap(Object [] input) {
heapArray = input;
capacity = input.length;
size = input.length;
heapify();
}
public MinHeap(int length) {
capacity = length;
heapArray = new Object[size];
size = 0;
}
@Override
public boolean compare(int j, int i ) {
if((Integer)heapArray[j] > (Integer)heapArray[i]) {
return true;
}
return false;
}
public static void main(String[] args) {
Object [] array = {6,5,4,3,2,1};
MinHeap myHeap = new MinHeap(array);
myHeap.display();
}
}