堆和普通树的区别
堆并不能取代二叉搜索树,它们之间有相似之处也有一些不同。我们来看一下两者的主要差别:
节点的顺序。在二叉搜索树中,左子节点必须比父节点小,右子节点必须必比父节点大。但是在堆中并非如此。在最大堆中两个子节点都必须比父节点小,而在最小堆中,它们都必须比父节点大。
内存占用。普通树占用的内存空间比它们存储的数据要多。你必须为节点对象以及左/右子节点指针分配额为是我内存。堆仅仅使用一个数据来村塾数组,且不使用指针。
平衡。二叉搜索树必须是“平衡”的情况下,其大部分操作的复杂度才能达到O(log n)。你可以按任意顺序位置插入/删除数据,或者使用 AVL 树或者红黑树,但是在堆中实际上不需要整棵树都是有序的。我们只需要满足对属性即可,所以在堆中平衡不是问题。因为堆中数据的组织方式可以保证O(log n) 的性能。
搜索。在二叉树中搜索会很快,但是在堆中搜索会很慢。在堆中搜索不是第一优先级,因为使用堆的目的是将最大(或者最小)的节点放在最前面,从而快速的进行相关插入、删除操作。
package com.myproject.demo.算法;
import java.util.Arrays;
/**
* 堆是一个完全二叉树,初始建堆时间复杂度O(n)
* 每次重建堆复杂度是O(log2^n)因为排序需要n次所以时间复杂度是O(nlog2^n)
* 最优算法是从一个节点元素任意放置的二叉树开始,自底向上对每一个子树执行删除根节点时的Max-Heapify算法(这是对最大堆而言)使得当前子树成为一个二叉堆。具体而言,假设高度为h的子树均已完成二叉堆化,那么对于高度为h+1的子树,把其根节点沿着最大子节点的分枝做调整,最多需要h步完成二叉堆化。可以证明,这个算法的时间复杂度为O(n)
* 时间复杂度为 T(n)<=O(n)+(n-1)*O(log2(n)),即等于第一次构造最大堆操作 加上 后面n-1次构造堆操作
* 最后得到的T(n)=O(nlog2(n)).
* java优先队列priorityQueen用的就是堆排序,因为他每次只要取一个元素复杂度O(log2^n),所以不使用快速排序O(nlogn)等其他的排序算法
*/
public class 堆排序 {
public static void main(String args[]) {
int[] a = new int[]{16, 25, 34, 27, 30, 5, 7, 4, 41, 55};
堆排序.heapSort(a);
System.out.println(Arrays.toString(a));
}
static int parent(int i) {
return (i - 1) / 2;
}
static int left(int i) {
return 2 * i + 1;
}
static int right(int i) {
return 2 * i + 2;
}
static void maxHeapfy(int[] a, int i, int heapSize) { //数组a,第i个结点,heapSize是数组种实际要排序的元素的长度
int left = left(i); //有的时候能够递归到叶子结点,叶子结点无后继,下面两个if都注意到了这一点
int right = right(i);
int largest = i;
if (left < heapSize && a[left] > a[largest]) { //
largest = left;
}
if (right < heapSize && a[right] > a[largest]) {
largest = right;
}
if (largest != i) { //把最大值给父结点
a[largest] = a[largest] ^ a[i];
a[i] = a[largest] ^ a[i];
a[largest] = a[largest] ^ a[i];
maxHeapfy(a, largest, heapSize); //发生交换之后还要保证大根堆性质
}
}
static void buildMaxHeap(int[] a, int heapSize) {
for (int i = (heapSize - 2) / 2; i >= 0; i--) {
maxHeapfy(a, i, heapSize);
}
}
static void heapSort(int[] a) {
int len = a.length;
buildMaxHeap(a, len); //初始建堆
a[len - 1] = a[0] ^ a[len - 1]; //交换
a[0] = a[0] ^ a[a.length - 1];
a[len - 1] = a[0] ^ a[len - 1];
for (int i = 1; i < len - 1; i++) { //初始建堆之后还要排a.length-2次
maxHeapfy(a, 0, len - i);
a[len - 1 - i] = a[0] ^ a[len - 1 - i]; //交换
a[0] = a[0] ^ a[len - 1 - i];
a[len - 1 - i] = a[0] ^ a[len - 1 - i];
}
}
}