排序算法中堆排序是比较复杂的一种,因为它利用到了树结构,它是一种选择排序,与普通的选择排序不同,普通的选择是每次遍历数组去选择最大或最小的数,堆排序是利用完全二叉树结构.
堆排序是一种不稳定的排序.时间复杂度为:平均O(nlog2n),最坏 O(nlog2n),最好情况 O(nlog2n),空间复杂度O(1)
排序思想:其中每个结点的关键字都不大于其孩子结点的关键字,这样的堆称为小根堆。
其中每个结点的关键字都不小于其孩子结点的关键字,这样的堆称为大根堆。
举例来说,对于n个元素的序列{R0, R1, … , Rn}当且仅当满足下列关系之一时,称之为堆:
(1) Ri <= R2i+1 且 Ri <= R2i+2 (小根堆)
(2) Ri >= R2i+1 且 Ri >= R2i+2 (大根堆)
图片与排序思想参考:静默虚空博客
接下来我们分析算法来进行大根堆的排序算法,就是要保证树当中的每个父节点的值都必须要大于子节点,我们可以先找出有子节点的节点,然后从该节点开始向前遍历所有节点,分别判断节点的左右子节点的值,如果子节点大于父节点,交换该子节点与父节点.这样到根遍历结束,能够发现根节点为最大值,然后将根节点与最后一个节点(广度遍历)交换,然后从倒数第二个节点开始向前遍历,重复此操作.
public static void heapSort(int[] array) {
for (int i = 0; i < array.length - 1; i++) {
// 构造堆
constructHeap(array, array.length - i - 1);
// 交换根(最大值)与最后一位
exchange(array, array.length - i - 1);
print(array, i); // 打印
}
}
// 构造堆
public static void constructHeap(int[] array, int length) {
int parent = (length - 1) / 2;
for (int i = parent; i >= 0; i--) {
// 肯定存在左孩纸 比较子孩纸与父节点的大小
int child = i * 2 + 1;
if (array[child] > array[i]) {
swap(array, i, child);
}
if ((child = i * 2 + 2) <= length) {
// 存在右孩纸
if (array[child] > array[i]) {
swap(array, i, child);
}
}
}
}
public static void exchange(int[] array, int end) {
int temp = array[0];
array[0] = array[end];
array[end] = temp;
}
// 打印
public static void print(int[] array, int index) {
System.out.print("第 " + index + " 次排序后: ");
for (int i : array) {
System.out.print(i + " ");
}
System.out.println("\n");
}
public static void swap(int[] array, int index1, int index2) {
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
测试:
int[] array = new int[] { 1, 4, 5, 12, 13, 14, 18 };// 5 5 5 14 14 14 18
heapSort(array);
第 0 次排序后: 14 1 13 4 12 5 18
第 1 次排序后: 5 12 13 1 4 14 18
第 2 次排序后: 4 5 12 1 13 14 18
第 3 次排序后: 1 4 5 12 13 14 18
第 4 次排序后: 4 1 5 12 13 14 18
第 5 次排序后: 1 4 5 12 13 14 18