一、概念
- 堆是一种完全二叉树(适合用数组存储)
- 堆中每个节点的值都必须大于等于(或小于等于)其子树中每个节点的值
- 对于每个节点的值都大于等于子树中每个节点的值的堆,叫"大顶堆",对于每个节点的值都小于等于子树中每个节点的值,叫"小顶堆"
二、如何实现一个堆?
- 一般二叉树都是通过链表或者数组来存储,针对完全二叉树,用数组存储比较好,因为不需存储左右节点的指针,单纯通过数组下标就可找到
三、代码实现
package DataStructer.Tree;
public class HeadTree_zhu {
private int[] a;
private int n;
private int count;
public HeadTree_zhu(int capacity){
this.a = new int[capacity + 1];
this.n = capacity;
this.count = 0;
}
public void insert(int data){
if (count >= n){
return;
}
count++;
int i = count;
a[i] = data;
while (i / 2 > 0 && a[i] > a[i / 2]){
swap(a,i,i / 2);
i = i / 2;
}
}
public void delete(){
if (count == 0){
return;
}
a[1] = a[count];
count--;
heapify(a,count,1);
}
private void heapify(int[] a,int n,int i){
while (true){
int index = i;
if (2 * i <= n && a[i] < a[2 * i]){
index = 2 * i;
}
if (2 * i + 1 <= n && a[index] < a[2 * i + 1]){
index = 2 * i + 1;
}
if (index == i){
break;
}
swap(a,index,i);
i = index;
}
}
public static void swap(int[] a,int i,int j){
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
四、堆应用之堆排序
package DataStructer.Tree;
public class HeapSort_zhu {
public static void sort(int[] a){
buildHeap(a);
int k = a.length - 1;
while (k > 0){
swap(a,k,0);
k--;
heapify(a,k,0);
}
}
public static void buildHeap(int[] a){
for (int i = (a.length - 1) / 2;i >= 0;i--){
heapify(a,a.length - 1,i);
}
}
public static void heapify(int[] a,int n,int i){
while (true){
int index = i;
if (i * 2 + 1 <= n && a[i] < a[2 * i + 1]){
index = 2 * i + 1;
}
if (i * 2 + 2 <= n && a[index] < a[i * 2 + 2]){
index = i * 2 + 2;
}
if (index == i){
break;
}
swap(a,index,i);
}
}
public static void swap(int[] a,int i,int j){
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
五、实际开发中,为什么快排比堆排(时间复杂度都是O(nlogn),且都为原地排序)要好?
- 堆排序数据访问方式没有快排友好,对于快排来说,数据是顺序访问的;对于堆排来说,数据是跳着访问的;堆排一个重要的过程就是堆化,会依次访问下标为1、2、4、8等元素,这样对CPU缓存是不友好的
- 对于同样的数据,堆排序算法的数据交换次数要多于排序。堆排中有一个建堆的过程,会打乱数据原有的相对顺序,比如一组有序的数据通过建堆后,可能会更加无序