[关闭]
@Catyee 2021-08-14T12:23:32.000000Z 字数 2043 阅读 397

堆排序

数据结构与算法


一、堆排序,数组升序排列

leetcode 912. 排序数组

用堆进行排序,需要将数组建为大根堆,堆顶就是最大的元素,然后每次取出大根堆的堆顶,依次从数组的最后一个元素往前进行交换,交换完成之后还需要重新调整堆,最终数组有序。

注意,建成堆之后数组只是大体有序,并不是全局有序,所以不能直接认为建一个小根堆就完事了,一次建堆就是将数组变为大体有序的过程,从这个角度来看,堆排序也是插入排序的一种优化思路。

  1. public class Solution {
  2. public int[] sortArray(int[] nums) {
  3. buildHeap(nums);
  4. // 建完堆之后取出堆顶元素,和数组末位i的位置交换,重新调整堆
  5. int len = nums.length - 1;
  6. for (int i = len; i >= 1; i--) {
  7. swarp(nums, i, 0);
  8. len--;
  9. adjust(nums, 0, len);
  10. }
  11. return nums;
  12. }
  13. private void buildHeap(int[] nums) {
  14. // 这里必须从底层往上调整,简单做法是从最后一个节点往上调整,但实际上只要对有孩子的节点从下往上调整即可
  15. for (int i = nums.length/2; i >= 0; i--) {
  16. adjust(nums, i, nums.length - 1);
  17. }
  18. }
  19. // 数组中指定长度建堆
  20. private void adjust(int[] nums, int i, int len) {
  21. int right = (i + 1) * 2;
  22. int left = right - 1;
  23. int maxIndex = i;
  24. if (left <= len && nums[left] >= nums[i]) {
  25. maxIndex = left;
  26. }
  27. if (right <= len && nums[right] >= nums[maxIndex]) {
  28. maxIndex = right;
  29. }
  30. if (maxIndex == i) {
  31. return;
  32. }
  33. swarp(nums, i , maxIndex);
  34. adjust(nums, maxIndex, len);
  35. }
  36. public void swarp(int[] nums, int i, int j) {
  37. int temp = nums[i];
  38. nums[i] = nums[j];
  39. nums[j] = temp;
  40. }
  41. }

二、小根堆

剑指 Offer II 076. 数组中的第 k 大的数字

寻找数组中第k大的数,可以用一个大小为k的小根堆,让小根堆的根节点是最小的,先构建大小为k的小根堆,然后让剩余元素和根节点比较,如果比根节点还小,可以直接抛弃,不需要入堆,如果比根节点大,那么就用这个元素替换根节点,然后调整小根堆,调整的过程是让根节点和左右孩子节点总计三个节点中最小的那一个上浮,所以每次都要找到最小的节点,然后和根节点交换,如果发现最小的那个就是根节点,说明调整已经结束。

用数组来存储二叉树,小标为i的节点的有右孩子是(i+1)*2,左孩子下标就是右孩子减1

  1. // k > 1 && k < nums.length
  2. public class Solution {
  3. int[] heap = null; // 小根堆
  4. public int findKthLargest(int[] nums, int k) {
  5. heap = new int[k];
  6. // 先将k个元素入堆,然后再调整,不要边入堆边调整
  7. for (int i = 0; i < k; i++) {
  8. heap[i] = nums[i];
  9. }
  10. // 建堆
  11. buildHeap();
  12. for (int i = k; i < nums.length; i++) {
  13. setRoot(nums[i]);
  14. }
  15. return heap[0];
  16. }
  17. private void setRoot(int ver) {
  18. // 如果比根节点还小,就不用入堆了
  19. if (ver <= heap[0]) {
  20. return;
  21. }
  22. heap[0] = ver;
  23. adjust(0);
  24. }
  25. private void buildHeap() {
  26. // 这里必须从底层往上调整,简单做法是从最后一个节点往上调整,但实际上只要对有孩子的节点从下往上调整即可
  27. for (int i = heap.length/2 - 1; i >= 0; i--) {
  28. adjust(i);
  29. }
  30. }
  31. private void adjust(int i) {
  32. // 找到左右孩子的下标
  33. int right = (i + 1) * 2;
  34. int left = right - 1;
  35. // 找到父节点和左右孩子三个节点中最小的那一个
  36. if (left < heap.length && heap[left] < heap[i]) {
  37. min = left;
  38. }
  39. if (right < heap.length && heap[right] < heap[min]) {
  40. min = right;
  41. }
  42. // 如果最小的节点就是根节点,则调整结束,返回
  43. if (min == i) {
  44. return;
  45. }
  46. // 较小的节点和父节点交换(小数上浮)
  47. swap(i, min);
  48. // 递归调整下一层
  49. adjust(min);
  50. }
  51. private void swap(int i, int j) {
  52. int temp = heap[i];
  53. heap[i] = heap[j];
  54. heap[j] = temp;
  55. }
  56. }

三、大根堆

面试题 17.14. 最小K个数

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注