先看看堆(Heap)的概念:
对于集合元素 R = { k1 , k2 , k3 , ... kn };
如果满足
1:Ri >= R2i ;其中(2i <= n)
2: Ri >= R2i+1 ; 其中(2i+1 <= n )
或满足
1:Ri =< R2i ;其中(2i <= n)
2: Ri =< R2i+1 ; 其中(2i+1 <= n )
称为称为该序列是一个堆(最大堆 或 最小堆)。
堆排序的思想是对堆中的第一个元素和最后一个元素进行交换。则最后一个元素是有序的,前面 n-1 个元素是无序的,此时需要对前面n-1个元素进行一次堆调整(将序列中的最值调整到第一个位置,并且其左右子树是一个堆)。直到所有序列有序。
但是我们如何知道给的元素序列是一个堆呢?
答案是这个堆是在排序函数内部自己构造的,用户给定任何序列,程序内部将其构造为一个堆。
堆构造原理:
我们知道只包含一个元素的序列是一个堆。
对于长度为n的元素序列,从n/2开始以后的元素是没有孩子节点的(2i > n),他们都是叶子节点,所以[n/2 , n/2+1 , ... , n]都是一个堆。因此这些元素的父节点和他们进行一次调整就可以形成一个堆,同理只要对这些节点的父节点进行一次筛选就可以形成堆。直到所有的元素都进行了筛选后整个序列就称为一个堆。
筛选函数的实现:
private static void adjustHeap(int data[], int from, int to) { int i = from, j = 2 * (from + 1) - 1, tmp; while (i <to) { if (j + 1 < to && data[j] < data[j + 1]) { j++; } if (j < to && data[i] < data[j]) { tmp = data[i]; data[i] = data[j]; data[j] = tmp; } else { break; } i = j; j = 2 * (i+1) - 1; } }
在实现细节上需要注意的就是数组下标的映射。
堆构造实现代码:
for (int i = (len) / 2; i >= 0; i--) adjustHeap(data, i, len);
排序代码:
public static void heapSort(int data[]) { if (data == null || data.length <= 1) return; int len = data.length; for (int i = (len) / 2; i >= 0; i--) adjustHeap(data, i, len); int tmp, k; for (int i = 0; i < len; i++) { tmp = data[0]; k = len - i - 1; data[0] = data[k]; data[k] = tmp; adjustHeap(data, 0, k); } }