• 数据结构-堆


    一、什么是堆

    先了解两种特别的二叉树

    满二叉树

    除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树

    完全二叉树

    完全二叉树相对于满二叉树来说,最后一层叶子节点从左到右中间没有空缺的,像这样:

    计算机科学中,堆是一种基于树的数据结构,通常用完全二叉树实现。堆的特性如下

    • 大顶堆中,任意节点 C 与它的父节点 P 符合 p.value >= c.value
    • 小顶堆中,任意节点 C 与它的父节点 P 符合 p.value <= c.value
    • 最顶层的节点(没有父亲)称之为 root 根节点

    完全二叉树可以使用数组来表示,像下图这样

    如果从索引 0 开始存储节点数据,它的特征如下:

    • 节点i的父节点为 floor((i-1)/2)
    • 当 i>0 时,节点i的左子节点为2i+1,右子节点为2i+2,当然它们得小于size

    二、堆插入元素

    以小堆为例,在插入元素的时候,先把元素放到数组的最后,然后与父节点比较,如果比父节点大,就插入成功了,如果比父节点小就跟父节点交换位置,直到它比父节点大或到达跟节点为止。

    下面是代码实现

    1. /**
    2. *
    3. * @param e 添加的元素
    4. * @return 是否成功
    5. */
    6. public boolean offer (Integer e) {
    7. int i = size;
    8. size = i + 1;
    9. if (i == 0) {
    10. queue[i] = e;
    11. } else {
    12. siftUp(i, e);
    13. }
    14. return true;
    15. }
    16. /**
    17. * 1. 获取父节点
    18. * 2. 让元素与父节点比较,如果小于就交换位置,大于就结束
    19. * @param i 被添加元素的索引
    20. * @param e 被添加元素的值
    21. */
    22. public void siftUp(int i, Integer e) {
    23. queue[i] = e;
    24. while (i > 0) {
    25. int parent = (i - 1) / 2;
    26. if (e > queue[parent]) {
    27. break;
    28. }
    29. swap(i, parent);
    30. i = parent;
    31. }
    32. }

    三、出堆实现

    1. 弹出数组第一个元素返回
    2. 把数组最后一个元素放到第一位,然后对它元素进行下潜操作,下潜操作分为这3个步骤
    • 计算左子结点和右子节点
    • 左子节点和右子节点中较小的一个和父节点比较(子节点的index不能大于size)
    • 如果有比父节点小的就交换位置,直到到达叶子节点或者左子节点或右子节点没有小于父节点的就结束

    代码实现:

    1. /**
    2. * 1.返回第一个元素
    3. * 2.第一个元素与最后一个元素交换
    4. * 3.size--,最后一个元素置为null
    5. * 4.对第一个元素执行下潜操作
    6. */
    7. public Integer poll() {
    8. if (size == 0) {
    9. throw new RuntimeException("Null");
    10. }
    11. Integer result = queue[0];
    12. swap(0, size - 1);
    13. queue[--size] = null;
    14. siftDown(0);
    15. return result;
    16. }
    17. /**
    18. * 1. 计算左子结点和右子节点
    19. * 2. 左子节点和右子节点中较小的一个和父节点比较(子节点的index不能大于size)
    20. * 3. 如果有比父节点小的就交换位置,直到到达叶子节点或者左子节点或右子节点没有小于父节点的就结束
    21. * @param index 要下潜元素的索引
    22. */
    23. public void siftDown(int index) {
    24. int parent = index;
    25. int left = index * 2 + 1;
    26. int right = left + 1;
    27. if (left < size && queue[left] < queue[parent]) {
    28. parent = left;
    29. }
    30. if (right < size && queue[right] < queue[parent]) {
    31. parent = right;
    32. }
    33. // 说明找到更小的了
    34. if (parent != index) {
    35. swap(parent, index);
    36. siftDown(parent);
    37. }
    38. }

    四、完整代码

    1. public class Heap {
    2. private static final int DEFAULT_INITIAL_CAPACITY = 11;
    3. transient Integer[] queue;
    4. private int size = 0;
    5. public Heap() {
    6. queue = new Integer[DEFAULT_INITIAL_CAPACITY];
    7. }
    8. public static void main(String[] args) {
    9. Heap heap = new Heap();
    10. heap.offer(1);
    11. heap.offer(5);
    12. heap.offer(2);
    13. heap.offer(4);
    14. heap.offer(3);
    15. heap.poll();
    16. heap.poll();
    17. }
    18. /**
    19. *
    20. * @param e 添加的元素
    21. * @return 是否成功
    22. */
    23. public boolean offer (Integer e) {
    24. int i = size;
    25. size = i + 1;
    26. if (i == 0) {
    27. queue[i] = e;
    28. } else {
    29. siftUp(i, e);
    30. }
    31. return true;
    32. }
    33. /**
    34. * 1. 获取父节点
    35. * 2. 让元素与父节点比较,如果小于就交换位置,大于就结束
    36. * @param i 被添加元素的索引
    37. * @param e 被添加元素的值
    38. */
    39. public void siftUp(int i, Integer e) {
    40. queue[i] = e;
    41. while (i > 0) {
    42. int parent = (i - 1) / 2;
    43. if (e > queue[parent]) {
    44. break;
    45. }
    46. swap(i, parent);
    47. i = parent;
    48. }
    49. }
    50. /**
    51. * 1.返回第一个元素
    52. * 2.第一个元素与最后一个元素交换
    53. * 3.size--,最后一个元素置为null
    54. * 4.对第一个元素执行下潜操作
    55. */
    56. public Integer poll() {
    57. if (size == 0) {
    58. throw new RuntimeException("Null");
    59. }
    60. Integer result = queue[0];
    61. swap(0, size - 1);
    62. queue[--size] = null;
    63. siftDown(0);
    64. return result;
    65. }
    66. public void swap(int i, int j) {
    67. int temp = queue[i];
    68. queue[i] = queue[j];
    69. queue[j] = temp;
    70. }
    71. /**
    72. * 1. 计算左子结点和右子节点
    73. * 2. 左子节点和右子节点中较小的一个和父节点比较(子节点的index不能大于size)
    74. * 3. 如果有比父节点小的就交换位置,直到到达叶子节点或者左子节点或右子节点没有小于父节点的就结束
    75. * @param index 要下潜元素的索引
    76. */
    77. public void siftDown(int index) {
    78. int parent = index;
    79. int left = index * 2 + 1;
    80. int right = left + 1;
    81. if (left < size && queue[left] < queue[parent]) {
    82. parent = left;
    83. }
    84. if (right < size && queue[right] < queue[parent]) {
    85. parent = right;
    86. }
    87. // 说明找到更小的了
    88. if (parent != index) {
    89. swap(parent, index);
    90. siftDown(parent);
    91. }
    92. }
    93. }
  • 相关阅读:
    php在数字前面补0得到固定长度数字的两种方法
    MindManager2022全新正式免费思维导图更新
    数据化运营02 告别迷茫!北极星指标,为业务指明方向
    使用Postern实现Android设备的全局代理优劣势分析
    详解 Serverless 架构的 6 大应用场景
    linux平常总结
    Ajax:传统请求方式及ajax原理解析
    如何使用 AI与人工智能的定义、研究价值、发展阶段
    QService 服务 指令引用的“0x00000000”内存。该内存不能为“read“
    服务访问质量(QoS)介绍与技术 二
  • 原文地址:https://blog.csdn.net/qq_42008471/article/details/134280255