1. 步骤

  • 建立最大堆:

    • 从数组中间的位置,往前遍历每个元素,进行下沉操作。
    • 因为叶子节点占整个数量的一半,且叶子节点不需要下沉。
  • 交换根节点元素:

    • 最大堆建立完成后,根节点元素最大,此时将其与末尾元素交换,则该元素有序。
  • 再次进行下沉操作:

    • 此时根节点为原来的末尾元素,对该元素进行下沉操作。
    • 即在此建立最大堆。
  • 重复以上步骤,直到堆中只剩下一个元素。

2. 方法

2.1 获取左孩子的索引

  1. /**
  2. * 计算左孩子的index
  3. *
  4. * @param k 父亲节点的index
  5. * @return
  6. */
  7. private int leftChild(int k) {
  8. return 2 * k + 1;
  9. }

2.2 下沉操作

  • 因为每次都会将最大的元素放到最后,所以最后的元素是排好序的因此需要n来指定最后一个元素的位置,不参与下沉操作。
  • 让j指向左右孩子中较大的元素,使用数组索引的时候注意越界问题。
  • 如果父亲大于最大的孩子,则无需下沉,结束循环。
  • 否则交换节点
  1. /**
  2. * @param arr 原始数组
  3. * @param k 要下沉的节点的编号
  4. * @param n 最后的元素的index,闭区间
  5. */
  6. private void siftDown(int[] arr, int k, int n) {
  7. while (leftChild(k) <= n) {
  8. int j = leftChild(k);
  9. if (j + 1 <= n && arr[j] < arr[j + 1])
  10. j++;
  11. if (arr[k] >= arr[j])
  12. break;
  13. swap(arr, k, j);
  14. k = j;
  15. }
  16. }

3. 代码

  1. public class HeapSort{
  2. public void sort(int[] arr) {
  3. int n = arr.length - 1;
  4. // 建立最大堆
  5. for (int i = n / 2; i >= 0; i--) {
  6. siftDown(arr, i, n);
  7. }
  8. // 交换首尾元素并下沉
  9. while (n > 0) {
  10. swap(arr, 0, n--);
  11. siftDown(arr, 0, n);
  12. }
  13. }
  14. private int leftChild(int k) {
  15. return 2 * k + 1;
  16. }
  17. private void siftDown(int[] arr, int k, int n) {
  18. while (leftChild(k) <= n) {
  19. int j = leftChild(k);
  20. if (j + 1 <= n && arr[j] < arr[j + 1])
  21. j++;
  22. if (arr[k] >= arr[j])
  23. break;
  24. swap(arr, k, j);
  25. k = j;
  26. }
  27. }
  28. }