堆排序详解与Java模版

堆排序 就是利用大根堆或者小根堆保证堆顶处一定是最大值或者最小值

先验知识:

  1. 使用数组保存一颗完全二叉树, 完全二叉树的节点 N = heapSize
  2. 二叉树的高度为log(heapSize) = logN
  3. 一个节点i, 则i的左孩子是 (2 * i + 1), 右孩子是 (2 * i + 2), 父亲节点是((i - 1) / 2)
  4. heapInsert(): 向堆中插入新的数字a到heapSize处, 让a一直和其父亲比较大小, 超过就取代父亲节点, 直到找到合适位置
  5. heapify(index): 堆化, 从index向左右孩子中较大的一个比较, 如果小于则交换, 直到找到合适的位置
    1)即先将数组的每个元素一次heapInsert()
    2)然后将堆顶元素和最后一个元素交换位置, heapSize–
    3)此时继续对堆顶元素做heapify()操作, 然后继续交换
    4)重复上面步骤, 每次都会将一个元素放到最后, 完成排序

注意:

  • 顺序排序需要维护大根堆, 逆序排序需要维护小根堆, 因为需要交换堆顶元素到最后, 所以其实是相反的
  • 时间复杂度是NlogN, 每次heapInsert()或者heapify()都是logN的, 然后一共需要遍历两次数组即2N

Java大根堆实现顺序排序模版

import java.util.Arrays;

public class HeapSort {
   public static void main(String[] args) {
       int[] arr = {0, -3, 5, 3, 2, 565, 67, 4, 2, 3, 23, 423, 43, 53, 52, 6, 4, 2, 32, 452, 23, 23, 5, 54, 9, 6};
       System.out.println(Arrays.toString(arr));
       heapSort(arr);
       System.out.println(Arrays.toString(arr));
   }

   // 默认使用大根堆 做顺序排序
   private static void heapSort(int[] arr) {
       if (arr.length < 2) {
           return;
       }

       // 先做一次heapInsert操作
       for (int i = 0; i < arr.length; i++) {
           heapInsert(arr, i);
       }

       // 再交换堆顶元素到最后面 然后对交换上来的元素继续做heapify()
       int heapSize = arr.length - 1;
       while (heapSize > 0) {
           // 交换堆顶元素
           swap(arr, 0, heapSize);
           heapSize--; // 最后一个已经排序好了 不用动它
           // 对交换到堆顶的元素做heapify
           heapify(arr, 0, heapSize);
       }
   }

   // 对heapSize的元素做heapInsert()操作,向上比较 大根堆
   private static void heapInsert(int[] arr, int heapSize) {
       // 取代比它小的父亲节点
       while (arr[heapSize] > arr[(heapSize - 1) / 2]) {
           swap(arr, heapSize, (heapSize - 1) / 2);
           heapSize = (heapSize - 1) / 2;
       }
   }

   // 对小于heapSize的任意节点index做heapify,向下比较 大根堆
   private static void heapify(int[] arr, int index, int heapSize) {
       // 先判断是否有孩子
       int left = 2 * index + 1;
       while (left <= heapSize) {
           // 此时左孩子left肯定存在, 看右孩子left + 1是否存在
           int largest = left + 1 <= heapSize && arr[left + 1] > arr[left]
                   ? left + 1 : left;
           // 比较父亲和较大的孩子之间最大的一个
           largest = arr[index] > arr[largest] ? index : largest;

           // 如果 父亲更大 表示这个位置合适 不用交换
           if (largest == index) {
               break;
           }

           // 孩子更大 交换
           swap(arr, index, largest);

           // 将当前的index变成较大的孩子节点的索引
           index = largest;
           // 继续判断左孩子
           left = 2 * index + 1;
       }
   }

   private static void swap(int[] arr, int i, int j) {
       int tmp = arr[i];
       arr[i] = arr[j];
       arr[j] = tmp;
   }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值