package algorithms;
import java.util.Random;
public class MaxHeapify {
private static int heapSize;
public static void main(String[] args) {
Random rand = new Random();
int[] a = new int[rand.nextInt(10)];
for (int i = 0; i < a.length; i++)
a[i] = rand.nextInt(100);
heapSort(a);
for (int i : a)
System.out.print(i + " ");
}
private static void heapSort(int[] a) {
heapSize = a.length;
buildMaxHeap(a);
for (int i = a.length - 1; i >= 1; i--) {
swap(a, i, 0);
heapSize = heapSize - 1;
maxHeapify(a, 0);
}
}
private static void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
private static void buildMaxHeap(int[] a) {
for (int i = a.length / 2; i >= 0; i--) {
maxHeapify(a, i);
}
}
private static void maxHeapify(int[] a, int i) {
int l = left(i);
int r = right(i);
int largest = i;
if (l < heapSize && a[l] > a[i])
largest = l;
else
largest = i;
if (r < heapSize && a[r] > a[largest])
largest = r;
if (largest != i) {
swap(a, i, largest);
maxHeapify(a, largest);
}
}
private static int left(int i) {
return 2 * i;
}
private static int right(int i) {
return 2 * i + 1;
}
}
堆排序和合并排序一样,是一种时间复杂度为O(nlgn)的算法,同时和插入排序一样,是一种就地排序算法(不需要额外的存储空间)。堆排序需要用到一种被称为最大堆的数据结构,与java或者lisp的gc不一样,这里的堆是一种数据结构,他可以被视为一种完全二叉树,即树里面除了最后一层其他层都是填满的。
我们现在有一个数组A,大小是n,假设其中元素按照完全二叉树的方式排列。如何将其构造成一个最大堆?首先我们知道最大堆的每个子树都符合最大堆的性质(根节点值大于所有子节点)。同时我们知道序号为(n/2+1)~n的元素都是叶子节点(因为其子女节点的序号都大于n,即说明没有子女节点),因此我们构建最大堆的操作就在序号为1~n/2的元素内进行(其他元素已满足最大堆性质)。我们定义如下操作maxify(i):将以i位置节点为根的子树改造成最大堆。其操作内容如下:对于每个节点i,我们考察他与子女节点的大小,如果他比某个子女节点小,则将他与子女节点中最大的那个互换位置,然后在相应的子女节点位置重复操作,直到到达堆的叶子节点或者考察的位置比子女节点的值都要大为止。由此可知我们构造最大堆buildmaxheap的过程就是在每个内部节点上调用maxify过程,依次到树的根部,此时其左右子树都是最大堆,现在在根节点调用maxify即完成了最大堆的构造。