返回首页返回书籍《Hello 算法》
目录

目录 · CONTENTS

  1. 01序
  2. 02前言
  3. 03关于本书
  4. 04如何使用本书
  5. 05小结
  6. 06初识算法
  7. 07算法无处不在
  8. 08算法是什么
  9. 09小结
  10. 10复杂度分析
  11. 11算法效率评估
  12. 12迭代与递归
  13. 13时间复杂度
  14. 14空间复杂度
  15. 15小结
  16. 16练习
  17. 17数据结构
  18. 18数据结构分类
  19. 19基本数据类型
  20. 20数字编码
  21. 21字符编码
  22. 22小结
  23. 23练习
  24. 24数组与链表
  25. 25数组
  26. 26链表
  27. 27列表
  28. 28内存与缓存
  29. 29小结
  30. 30练习
  31. 31栈与队列
  32. 32栈
  33. 33队列
  34. 34双向队列
  35. 35小结
  36. 36练习
  37. 37哈希表
  38. 38哈希表
  39. 39哈希冲突
  40. 40哈希算法
  41. 41小结
  42. 42练习
  43. 43树
  44. 44二叉树
  45. 45二叉树遍历
  46. 46二叉树数组表示
  47. 47二叉搜索树
  48. 48AVL 树
  49. 49小结
  50. 50练习
  51. 51堆
  52. 52堆
  53. 53建堆操作
  54. 54Top-k 问题
  55. 55小结
  56. 56练习
  57. 57图
  58. 58图
  59. 59图的基础操作
  60. 60图的遍历
  61. 61小结
  62. 62练习
  63. 63搜索
  64. 64二分查找
  65. 65二分查找插入点
  66. 66二分查找边界
  67. 67哈希优化策略
  68. 68重识搜索算法
  69. 69小结
  70. 70练习
  71. 71排序
  72. 72排序算法
  73. 73选择排序
  74. 74冒泡排序
  75. 75插入排序
  76. 76快速排序
  77. 77归并排序
  78. 78堆排序
  79. 79桶排序
  80. 80计数排序
  81. 81基数排序
  82. 82小结
  83. 83练习
  84. 84分治
  85. 85分治算法
  86. 86分治搜索策略
  87. 87构建二叉树问题
  88. 88汉诺塔问题
  89. 89小结
  90. 90练习
  91. 91回溯
  92. 92回溯算法
  93. 93全排列问题
  94. 94子集和问题
  95. 95n 皇后问题
  96. 96小结
  97. 97练习
  98. 98动态规划
  99. 99初探动态规划
  100. 100动态规划问题特性
  101. 101动态规划解题思路
  102. 1020-1 背包问题
  103. 103完全背包问题
  104. 104编辑距离问题
  105. 105小结
  106. 106练习
  107. 107贪心
  108. 108贪心算法
  109. 109分数背包问题
  110. 110最大容量问题
  111. 111最大切分乘积问题
  112. 112小结
  113. 113练习
  114. 114附录
  115. 115编程环境安装
  116. 116一起参与创作
  117. 117术语表
  118. 118参考文献
  119. 119纸质书
第 56 / 119 节

第 56 节 · SECTION 56

练习

知识巩固

数字 10 入堆后怎样调整

数组 [9, 7, 8, 3, 5] 表示一个大顶堆。现在将数字 10 入堆。

  1. 先把 10 添加到数组末尾,它的父节点值是多少?
  2. 从新节点开始向上堆化,写出每次交换后的数组。
  3. 最终的堆顶元素是什么?一共交换了几次?

参考答案

  1. 10 添加后的索引为 5,其父节点索引为 $\lfloor(5-1)/2\rfloor=2$,父节点值为 8。

  2. 10 大于 8,第一次交换后为 [9, 7, 10, 3, 5, 8]; 10 又大于父节点 9,第二次交换后为 [10, 7, 9, 3, 5, 8]。 此时 10 已到达根节点,堆化结束。

  3. 最终堆顶为 10,共交换 2 次。

检查小顶堆的父子关系

数组 [1, 4, 3, 7, 6, 2] 表示一棵完全二叉树。小顶堆要求每个父节点都不大于它的孩子。 对索引 $i$,左、右孩子索引分别为 $2i+1$ 和 $2i+2$。

  1. 索引 2 的孩子索引和值分别是什么?
  2. 索引 2 处的节点值为 3,它与孩子节点之间是否违反小顶堆规则?如果违反,应交换哪两个元素?
  3. 根据第 2 问的判断:若违反规则,写出交换后的数组;若不违反,说明无须交换。最后检查其余父子关系。

参考答案

  1. 索引 2 的左孩子索引是 5、值为 2;右孩子索引是 6,但数组长度为 6,因此右孩子不存在。

  2. 父节点值 3 大于孩子值 2,违反小顶堆规则,应交换索引 2 和索引 5 的元素。

  3. 交换后得到 [1, 4, 2, 7, 6, 3]。逐一检查: 1 ≤ 4、1 ≤ 2;4 ≤ 7、4 ≤ 6;2 ≤ 3。 所有父节点都不大于自己的孩子,因此现在满足小顶堆规则。

用小顶堆保留最大的三个数

要从数据流 [4, 1, 7, 3, 8] 中保留最大的 3 个数,可以维护一个大小不超过 3 的小顶堆。

先把前 3 个数依次放入小顶堆。堆满后,每读入一个新数: 如果它大于堆顶,就删除堆顶并放入新数;否则堆保持不变。

请写出每次读入数字后堆中保留的数字和堆顶。 只需把保留的数字写成集合,不要求写出它们在堆数组中的排列。

参考答案

每次读入数字后的结果如下:

读入数字 保留的数字 堆顶
4 {4} 4
1 {1, 4} 1
7 {1, 4, 7} 1
3 {3, 4, 7} 3
8 {4, 7, 8} 4

堆满后,堆顶是当前保留数字中最小的一个。只有新数字大于堆顶时, 才用新数字替换堆顶。最终保留的 {4, 7, 8} 正是最大的 3 个数。

编程练习

寻找数组中的第 k 大元素

给定整数数组 nums 和整数 $k$,其中 $1 \le k \le n$,$n$ 是数组长度。把数组按从大到小排列后,请返回第 $k$ 个位置上的元素。

重复元素需要分别计数。例如,[5, 5, 2] 的第 2 大元素仍是 5。请使用一个大小不超过 $k$ 的小顶堆完成。

解题提示

  1. 第 k 大元素是最大的 k 个数中最小的那个
  2. 每个数先入小顶堆,大小超过 k 时弹出最小值
  3. 遍历结束时堆中保留最大的 k 个数,堆顶就是答案

LeetCode 题目解析

说明: 链接中的题解讲解了快速排序和快速选择,没有使用堆;本练习请按照正文 8.3 节使用大小不超过 k 的小顶堆完成