算法步骤

  • 首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
  • 将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
  • 将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

动图演示

heapSort.gif

代码

  1. //堆排序
  2. public static void heapSort(int[] arr) {
  3. //构造大根堆
  4. heapInsert(arr);
  5. int size = arr.length;
  6. while (size > 1) {
  7. //固定最大值
  8. swap(arr, 0, size - 1);
  9. size--;
  10. //构造大根堆
  11. heapify(arr, 0, size);
  12. }
  13. }
  14. //构造大根堆(通过新插入的数上升)
  15. public static void heapInsert(int[] arr) {
  16. for (int i = 0; i < arr.length; i++) {
  17. //当前插入的索引
  18. int currentIndex = i;
  19. //父结点索引
  20. int fatherIndex = (currentIndex - 1) / 2;
  21. //如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点
  22. //然后继续和上面的父结点值比较,直到不大于父结点,则退出循环
  23. while (arr[currentIndex] > arr[fatherIndex]) {
  24. //交换当前结点与父结点的值
  25. swap(arr, currentIndex, fatherIndex);
  26. //将当前索引指向父索引
  27. currentIndex = fatherIndex;
  28. //重新计算当前索引的父索引
  29. fatherIndex = (currentIndex - 1) / 2;
  30. }
  31. }
  32. }
  33. //将剩余的数构造成大根堆(通过顶端的数下降)
  34. public static void heapify(int[] arr, int index, int size) {
  35. int left = 2 * index + 1;
  36. int right = 2 * index + 2;
  37. while (left < size) {
  38. int largestIndex;
  39. //判断孩子中较大的值的索引(要确保右孩子在size范围之内)
  40. if (arr[left] < arr[right] && right < size) {
  41. largestIndex = right;
  42. } else {
  43. largestIndex = left;
  44. }
  45. //比较父结点的值与孩子中较大的值,并确定最大值的索引
  46. if (arr[index] > arr[largestIndex]) {
  47. largestIndex = index;
  48. }
  49. //如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环
  50. if (index == largestIndex) {
  51. break;
  52. }
  53. //父结点不是最大值,与孩子中较大的值交换
  54. swap(arr, largestIndex, index);
  55. //将索引指向孩子中较大的值的索引
  56. index = largestIndex;
  57. //重新计算交换之后的孩子的索引
  58. left = 2 * index + 1;
  59. right = 2 * index + 2;
  60. }
  61. }
  62. //交换数组中两个元素的值
  63. public static void swap(int[] arr, int i, int j) {
  64. int temp = arr[i];
  65. arr[i] = arr[j];
  66. arr[j] = temp;
  67. }

参考:堆排序算法(图解详细流程)