应用场景:动态维护最小值或最大值
堆排序.gif

含义

一棵完全二叉树
大根堆:根元素大于等于左右子树的所有值
小根堆:根元素小于等于左右子树的所有值

存储方式

用一个数组就可以存储,从下标为1的位置开始,再用一个值 size 存储当前堆的大小。
假设指向根节点的下标为x,它的左子树根节点下标为 2 * x ,它的右子树下标为 2 * x + 1

操作

  1. //向上
  2. void up(int x) {
  3. //向上递归将x与父节点交换(如果需要交换的话)
  4. }
  5. //向下
  6. void down(int x) {
  7. //向下递归将x与左右子节点交换(如果需要的话)
  8. }

问题

各种操作的方法:

  1. //1. 插入一个数
  2. heap[++size] = x;
  3. up(size);
  4. //2. 求集合中的最小值
  5. heap[1];
  6. //3. 删除最小值
  7. swap(heap, 1, size);
  8. size--;
  9. down(1);
  10. //4. 删除任意元素
  11. swap(heap, k, size);
  12. size--;
  13. down(k);
  14. up(k);
  15. //修改任意元素
  16. heap[k] = x;
  17. down(k);
  18. up(k);

堆初始化的时间复杂度?
自下而上:O(n)
自上而下:O(nlogn)
image.png

例题

838. 堆排序

输入一个长度为 n 的整数数列,从小到大输出前 m 小的数。
输入格式
第一行包含整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
输出格式
共一行,包含 m 个整数,表示整数数列中前 m 小的数。
数据范围
1≤m≤n≤105,
1≤数列中元素≤109
输入样例:

  1. 5 3
  2. 4 5 1 3 2

输出样例:

  1. 1 2 3
  1. // 大根堆实现,不过本题要用小根堆,无所谓了,不针对该题
  2. import java.util.*;
  3. public class Main {
  4. static int n, m;
  5. public static void main(String[] args) {
  6. Scanner sc = new Scanner(System.in);
  7. n = sc.nextInt();
  8. m = sc.nextInt();
  9. Heap heap = new Heap(n);
  10. int[] a = new int[n];
  11. for (int i = 0; i < n; i++)
  12. a[i] = sc.nextInt();
  13. heap.init(a);
  14. for (int i = 0; i < m; i++) {
  15. System.out.print(heap.pop() + " ");
  16. }
  17. }
  18. }
  19. class Heap {
  20. int n, size;
  21. int[] a;
  22. Heap(int n) {
  23. this.n = n + 1;
  24. a = new int[n + 1];
  25. }
  26. void init(int[] b) {
  27. size = b.length;
  28. for (int i = 1; i <= size; i++) {
  29. a[i] = b[i - 1];
  30. }
  31. // 自下而上初始化 o(n)
  32. for (int i = size / 2; i > 0; i--) {
  33. down(i);
  34. }
  35. // 自上而下初始化 O(nlogn)
  36. // for (int i = 1; i <= size; i++) {
  37. // up(i);
  38. // }
  39. }
  40. int pop() {
  41. int res = a[1];
  42. a[1] = a[size];
  43. size--;
  44. return res;
  45. }
  46. void down(int idx) {
  47. int left = idx * 2, right = idx * 2 + 1;
  48. if (idx <= size) {
  49. int maxIdx = idx;
  50. if (left <= size && a[left] > a[maxIdx]) maxIdx = left;
  51. if (right <= size && a[right] > a[maxIdx]) maxIdx = right;
  52. if (maxIdx != idx) {
  53. swap(idx, maxIdx);
  54. down(maxIdx);
  55. }
  56. }
  57. }
  58. void up(int idx) {
  59. if (idx / 2 > 0 && a[idx] > a[idx / 2]) {
  60. swap(idx, idx / 2);
  61. up(idx / 2);
  62. }
  63. }
  64. void swap(int i, int j) {
  65. int t = a[i];
  66. a[i] = a[j];
  67. a[j] = t;
  68. }
  69. }