堆结构(Heap)、完全二叉树的概念
从左往右依次铺满的二叉树就是完全二叉树。
堆结构,通常我们有大根堆或者小根堆。以大根堆为例,它首先是一个完全二叉树,且每个叶子节点都比父节点的值,根节点是整个树中最大值。
用数组来表示一棵完全二叉树
假设数组为 arr 我们用 i 来代表数组的下标,数组中第i个数的值为arr[i]
下标为i的节点对应的 左节点 2i+1 右节点则是 2i+2
而 i 的父节点下标为 (i - 1)/2
arr[0]则作为这棵树的根节点。
如何从完全二叉树变成大根堆
利用数组来表示完全二叉树时候,我们就可以轻松通过下标关系来获取到父子节点。当然,在这之前先判断是否越界问题。
实现堆最重要的两个方法:heapify 以及 heapInsert
Heapify
可以理解为在一棵完全二叉树中(数组表示)对某个节点进行“堆化”,使得这个节点往下的2个子节点都比父节点小(大/如果是小根堆),形成一个局部的堆。如果heapify自子节点起开始作用,一直往上,则完成一系列动作后,这棵完全二叉树就是大(小)根堆了。
//对index为i的子树进行堆化处理
void MaxHeapify(int i) {
int l = lChild(i);
int r = rChild(i);
int largest = i;
if (l < heapSize && arr[l] > arr[i])
largest = l;
if (r < heapSize && arr[r] > arr[largest])
largest = r;
if (largest != i) {
int temp = arr[i];
arr[i] = arr[largest];
arr[largest] = temp;
MaxHeapify(largest);
}
}
heapInsert
维护当前堆状态,当从二叉树尾部插入一棵树时,heapInsert会在插入后向上比较如果大于当前父节点则向上交换,直到找不到大于它的或者以及时根节点了。这时,新加入的这个节点就没有打破堆结构。
一些性能上的数据
如何排序,复杂度问题
堆排序步骤:
-
给定一个数组,从中间(i/2 -1)开始往根节点依次进行局部的“堆化”(heapify),将这个完全二叉树(给定待排序 的数组)变成一个堆。
-
循环遍历堆大小,将根节点取出存到一个新同等大小的数组。每次取出根节点后,将最后一个节点放到根节点上,然后对根节点进行一次“堆化”(heapify)操作。整个遍历过后,便会获得一个排好序的数组。
堆数据结构时间复杂度:
访问最值 O(1),插入和删除 O(log n), 堆排序O(nlog n)
关于堆的应用
-
优先队列
-
堆排序
-
内存管理
-
一下关于图的算法问题
代码
附上简单的堆结构代码:
package algorithm.heap;
public class MaxHeap {
private int[] heap;
private int size;
private int getParentPosition(int i){
return (i - 1) >> 1;
}
private int getLeftPosition(int i){
return 2 * i + 1;
}
private int getRightPosition(int i){
return 2 * i + 2;
}
public MaxHeap() {
heap = new int[16];
size = 0;
}
private void headInsert(int num,int index){
heap[index] = num;
while (index != 0 && heap[getParentPosition(index)] < heap[index]){
int pi = getParentPosition(index);
swap(index,pi);
index = pi;
}
}
public void add(int num){
if (++size >= heap.length) {
int[] newHeap = new int[heap.length << 1];
for (int i = 0; i < heap.length; i++) {
newHeap[i] = heap[i];
}
heap = newHeap;
}
headInsert(num,size - 1);
}
/**
* get maximum number
* @return
*/
public int get(){
return heap[0];
}
/**
* get and remove maximum number(root)
* @return
*/
public int pop(){
int res = get();
size--;
int cur = 0;
if (size > 0){
heap[0] = heap[size];
heapify(0);
}
return res;
}
private void heapify(int index){
int l = getLeftPosition(index);
int r = getRightPosition(index);
while (r < size && heap[index] < Math.max(heap[l],heap[r])){
index = heap[l] > heap[r] ? l : r;
swap(index,getParentPosition(index));
l = getLeftPosition(index);
r = getRightPosition(index);
}
if (heap[l]>heap[index]){
swap(l,index);
}
}
private void swap(int i,int j){
int temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
public int getSize(){
return size;
}
}
测试:
package algorithm.heap;
import java.util.PriorityQueue;
public class heapTest {
public static void main(String[] args) {
MaxHeap maxHeap = new MaxHeap();
maxHeap.add(12);
maxHeap.add(31);
maxHeap.add(29);
maxHeap.add(122);
maxHeap.add(88);
maxHeap.add(8);
while (maxHeap.getSize() != 0){
System.out.println(maxHeap.pop());
}
PriorityQueue<Integer> p = new PriorityQueue<>();
}
}