堆是一种特殊的数据结构,首先堆是一个完全二叉树,所有堆满足所有二叉树的特定,对于大顶堆,最大数应用再一位,所有如果向对一个数组排序,可以将堆顶和最后一个元素交换,之后再次调整堆,直到堆元素个数为1,所有堆排序算法很简单:
1. 将数组构建成一个堆(生序:大顶堆,降序:小顶堆)
2. 交换堆顶和最后一个元素
3. 迭代1,2步,直到所有元素排序完成
代码实现如下:
package algorithm.sort; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; /** * 堆排序 * 堆性值: * 大顶堆 a[i] >= a[i2+1] && a[i] >= a[i2+2] * 小顶堆 a[i] <= a[i2+1] && a[i] <= a[i2+2] * 先构建大顶堆或者小顶堆,之后交换堆顶和最后一个元素,在堆N-1个元素重新构建堆,直到堆有序 * 时间复杂度O(nlogn) */ public class HeapSort { public static AtomicInteger n = new AtomicInteger(0); public static void swatch(int[] arr, int l, int r) { int tmp = arr[l]; arr[l] = arr[r]; arr[r] = tmp; } /** * 构建大顶堆 算法实现: * 1. 从堆的最后一个非子节点开始(arr.length/2-1),直到第一个元素开始遍历 * 2. 让非子节点和它的子节点比较,选取出最大值 * 3. 更新最大值到当前非子节点 * 4. 再次使用以上步骤,更新子节点,直到最后一个非子节点 * * @param arr 需要构建的数组 * @param parentIndex 当前处理的非子节点 * @param endIndex 数组长度,该参数主要用于堆排序,构建堆其实不需要该参数 */ public static void adjuestHead(int[] arr, int parentIndex, int endIndex) { //保存当前节点 int tmp = arr[parentIndex]; // 非子节点第一个孩子 int childIndex = parentIndex * 2 + 1; while (childIndex < endIndex) { // 判断是否有两个孩子 if (childIndex + 1 < endIndex && arr[childIndex] < arr[childIndex + 1]) childIndex += 1; //如果父节点大于子节点,则直接退出 if (tmp >= arr[childIndex]) break; arr[parentIndex] = arr[childIndex]; //继续堆子节点调整 parentIndex = childIndex; childIndex = childIndex * 2 + 1; } arr[parentIndex] = tmp; } public static void maxHeadHeap(int[] arr) { // 第一个非叶子节点,从下至上,从右至左 for (int i = arr.length / 2 - 1; i >= 0; i--) { adjuestHead(arr, i, arr.length); } } public static void headSort(int[] arr) { //初始并构建大顶堆 maxHeadHeap(arr); for (int index = arr.length - 1; index >= 0; index--) { //交换堆顶和最后一个元素 swatch(arr, 0, index); //重新调整剩余堆元素 adjuestHead(arr, 0, index); } } public static void main(String[] args) { int data_len = 100000; int[] data = new int[data_len]; Random rd = new Random(); for (int index = 0; index < data_len; ) { data[index++] = rd.nextInt(10000); } System.out.println("生成数据完成"); // showData(data); long start = System.currentTimeMillis(); headSort(data); System.out.printf("堆排序算法\t运行时间%dms\n", (System.currentTimeMillis() - start)); showData(data); } public static void showData(int[] data) { for (int item : data) { System.out.printf("%d,", item); } System.out.println(); } }
堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素+重建堆组成,其中堆构建的时间复杂度O(n),交换并重建需要交换n-1次,重建堆过程,根据二叉树性值为nlogn,所以堆排序时间负责度一般认为是O(nlogn)