堆:堆是一棵完全二叉树,某个节点的值总是不大于(称为大根堆)或不小于其父节点(称为小根堆)的值。
【算法描述】
- 将待排序的n个元素按照堆的定义建初堆,并移除堆顶元素
- 调整剩余的元素序列(也就是去掉了堆顶元素的序列),将这n-1个元素重新建成一个新堆,再移除堆顶元素
- 重复执行步骤二,进行n-1次筛选,新筛选的堆会越来越小,最后所有的堆顶元素都会被移除,被移除的堆顶元素就是一个有序序列
下图中,是对{14,98,77,35,62,55}进行升序排列的过程
【算法实现】
public class Sort {
/* 建堆 */
public static void sift(int[] r, int k, int m) {// r[k..m]是以r[k]为根的完全二叉树,r[m]是最后一个结点
int temp = r[k];// 暂存根结点
int i = k;
int j = 2 * i + 1;// a[j]是a[i]的左节点
boolean finished = false;
while (j < m && !finished) {
/* 如果存在右子树,并且右子树大,那么就沿右分支筛选 */
if (j + 1 < m && r[j] < r[j + 1]) {
j += 1;
}
/* 不存在右子树或者根节点比左右子树都大的时候筛选完毕 */
if (temp > r[j]) {
finished = true;
} else {
r[i] = r[j];
i = j;
j = 2 * i + 1;
} /* 继续筛选 */
}
r[i] = temp;
}/* sift */
/* 建初堆 */
public static void heap(int[] r) {
int len = r.length;
for (int i = len / 2 - 1; i >= 0; i--) {
sift(r, i, len);
}
}/* heap */
public static void heapSort(int[] r) {
int len = r.length;
heap(r);
/* 重建堆 */
for (int m = len - 1; m > 0; m--) {
/* 堆顶元素与堆尾交换 */
int b = r[0];
r[0] = r[m];
r[m] = b;
sift(r, 0, m);
}
}/* heapSort */
public static void main(String[] args) {
int[] r = { 14, 98, 77, 35, 62, 55 };
Sort.heapSort(r);
for (int i : r) {
System.out.print(i + " ");
}
}
}
【算法分析】
堆排序是我们所知的唯一能够同时最优地利用空间和时间的方法,他的的时间主要耗费在建初堆和重建堆上,如果有n个元素,建初堆需调整的次数最多为4n次,时间复杂度为O(n),调整堆的次数不超过2n[log2(n)],所以堆排序在最坏情况下,时间复杂度为O(nlog2(n)),空间复杂度为O(1),但它是不稳定排序