堆排序
堆排序基本介绍
1) 堆排序是利用堆(heap)这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为 O(nlogn),它也是不稳定排序。
堆排序的基本思想是:
1. 将待排序序列构造成一个大(小)顶堆
2. 此时,整个序列的最大(小)值就是堆顶的根节点。
3. 将其与末尾元素进行交换,此时末尾就为最大(小)值。
4. 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小(大)值。如此反复执行,便能得到一个有序序列了。
堆的介绍
1. 必须是完全二叉树
2. 每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆
3. 每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
注意 : 没有求结点的左孩子的值和右孩子的值的大小关系,一般升序采用大顶堆,降序采用小顶堆
大顶堆:
小顶堆:
堆排序步骤图解
步骤一
构造初始堆。将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。
原始的数组 [4, 6, 8, 5, 9]
1) .假设给定无序序列结构如下
2) .此时我们从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点arr.length/2-1=5/2-1=1,也就是下面的 6 结点),从右至左,从下至上进行调整。
3) .找到第二个非叶节点 4,由于[4,9,8]中 9 元素最大,4 和 9 交换。
4) 这时,交换导致了子根[4,5,6]结构混乱,继续调整(通过递归方法完成),[4,5,6]中 6 最大,交换 4 和 6。
此时,我们就将一个无序序列构造成了一个大顶堆。
步骤二 将堆顶元素与末尾元素进行交换,使末尾元素最大。然后继续调整堆,再将堆顶元素与末尾元素交换
得到第二大元素。如此反复进行交换、重建、交换。
1) .将堆顶元素 9 和末尾元素 4 进行交换
2) .重新调整结构,使其继续满足堆定义
3) .再将堆顶元素 8 与末尾元素 5 进行交换,得到第二大元素 8.
4) 后续过程,继续进行调整,交换,如此反复进行,最终使得整个序列有序
代码实现
1. //要求将数组进行升序排序 2. public class HeapSort { 3. public static void main(String[] args) { 4. int[] arr = {11, 100, 108, -17, 125, 13, 114, 21}; 5. 6. heapSort(arr, arr.length); 7. 8. for (int data : arr) { 9. System.out.println(data); 10. } 11. } 12. 13. /** 14. * @param arr 要变成堆的数组 15. * @param len 数组的长度 16. * @param i 对哪一个节点进行堆化 17. * @TODO 将某棵完全二叉树进行堆化操作 18. */ 19. public static void heapify(int[] arr, int len, int i) { 20. if (i >= len) {//递归结束条件 21. return; 22. } 23. int left = i * 2 + 1;//i结点的左子结点 24. int right = i * 2 + 2;//i结点的右子结点 25. int max = i;//默认令下标为i的结点为最大值 26. if (left < len && arr[left] > arr[max]) {//找到当前二叉树左子结点与该节点的较大值 27. max = left; 28. } 29. if (right < len && arr[right] > arr[max]) {//将上面比较出的较大值与该节点的右子结点比较获取最大值 30. max = right; 31. } 32. if (max != i) {//如果max不等于i,说明i结点不是最大值,因此就需要将i结点值与最大值的结点进行交换 33. int temp = arr[max]; 34. arr[max] = arr[i]; 35. arr[i] = temp; 36. /*交换完成后,就可以保证i结点的二叉树是堆化的, 37. 但不能保证左右子结点的子树(i的左右子结点为父节点时) 38. 是否也是堆化因此需要递归操作进行之后的堆化*/ 39. heapify(arr, len, max); 40. 41. } 42. } 43. 44. /** 45. * 通过此方法就可将整棵树进行堆化 46. * 流程:从最后一个父节点(注意不是叶子节点)开始进行堆化,直到根节点 47. * 48. * @param arr 49. * @param len 50. */ 51. public static void buildHeap(int[] arr, int len) { 52. int lastNode = len - 1;//最后一个叶子节点 53. int i = (lastNode - 1) / 2;//最后一个结点的父节点,即最后一个父结点 54. for (int j = i; j >= 0; j--) {//从最后一个父节点开始进行堆化,直到根节点(j==0) 55. heapify(arr, len, j); 56. } 57. } 58. 59. /** 60. * 此方法能够将数组进行堆排序(有序(列)化) 61. * 62. * @param arr 要排序的数组 63. * @param len 要排序的数组长度 64. */ 65. private static void heapSort(int[] arr, int len) { 66. buildHeap(arr, len);//首先通过此方法将其堆化,变为大顶堆(arr[0]为树中最大值) 67. for (int i = len - 1; i > 0; i--) {//从最后一个节点开始,i可以不用等于0,因为最后一个数一定是有序的 68. int temp = arr[i]; 69. arr[i] = arr[0]; 70. arr[0] = temp;//将arr[0]与arr[i]进行交换,arr[i]之后都是确定有序的 71. heapify(arr, i, 0);//交换完成后,又不是大顶堆规则了,需要再次堆化,找到次大值。注意现在在0到i之中找出最大值 72. } 73. } 74. 75. }