返回首页返回书籍《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纸质书
第 70 / 119 节

第 70 节 · SECTION 70

练习

知识巩固

二分查找怎样缩小搜索区间

在有序数组 [2, 5, 8, 12, 16, 23, 38] 中查找 16。 使用双闭区间 [i, j],中点为 $m=i+(j-i)/2$(向下取整)。

请写出每轮的 (i, j, m)、中点元素,以及下一步如何缩小区间, 直到找到目标。

参考答案

每轮查找过程如下:

轮次 (i, j, m) 中点元素 下一步
1 (0, 6, 3) 12 12 < 16,令 i = 4
2 (4, 6, 5) 23 23 > 16,令 j = 4
3 (4, 4, 4) 16 找到目标,返回索引 4

数组有序,所以中点值小于目标时可以排除中点及其左侧; 中点值大于目标时可以排除中点及其右侧。

重复元素的左右边界

在数组 [1, 2, 2, 2, 4, 6] 中查找数字 2。 一名同学用二分查找在索引 2 找到目标后立即返回,并说: “索引 2 就是数字 2 的左边界。”

  1. 这名同学的说法是否正确?数字 2 的左、右边界分别是多少?请说明理由。
  2. 查找左边界时,若中点元素等于目标,接下来应继续搜索哪一侧?
  3. 查找右边界时又应继续搜索哪一侧?只需说明方向,无须写出完整的查找过程。

参考答案

  1. 这名同学的说法不正确。找到一个 2 后立即返回,只能保证找到某一个 2,不能保证找到最左或最右的 2。 本题的左边界为索引 1,右边界为索引 3。

  2. 查找左边界时,即使中点元素等于 2,也要继续搜索左侧, 例如在双闭区间写法中令 j = m - 1。

  3. 查找右边界时,中点元素等于 2 后应继续搜索右侧, 例如令 i = m + 1。

不同数据该选哪种搜索方法

请在“顺序查找、二分查找、哈希表”中,为下面三个场景选择合适的方法,并说明理由:

  1. 在 $10^7$ 个已经有序且不再变动的整数中反复查找,不额外建立其他数据结构;
  2. 在频繁插入、删除的数据集中反复判断某个键是否存在,不要求保持有序,也不进行范围查找;
  3. 在无序数组中只查找一次某个值。

参考答案

  1. 二分查找:数据有序且静态,$O(\log n)$ 且无须额外空间。

  2. 哈希表:当哈希函数能把键较均匀地分散到各桶时,插入、删除和按键查找的平均时间复杂度都是 $O(1)$。

  3. 直接从头到尾遍历:只查找一次时,排序或建立哈希表都要先处理整个数组, 并不会减少这一次任务的总工作量。

    选择取决于数据是否有序、是否允许建立额外结构、查询次数以及需要执行哪些操作。

编程练习

有序数组的二分查找

给定一个按严格递增顺序排列的整数数组 nums 和目标值 target。请使用二分查找找到 target:若存在,返回它的数组索引;若不存在,返回 -1。

解题提示

  1. 初始区间是 left = 0、right = n - 1,非空条件是 left <= right
  2. 用 mid = left + (right - left) // 2 计算中点
  3. 若 nums[mid] 小于 target,就把左边界移到 mid + 1;若 nums[mid] 大于 target,就把右边界移到 mid - 1;相等时立即返回

LeetCode 题目解析

有序数组的插入位置

给定一个按严格递增顺序排列的整数数组 nums 和目标值 target。

若 target 已在数组中,返回它的索引;否则,返回把 target 插入数组后仍能保持严格递增顺序的位置。答案可能是 0,也可能等于数组长度。请使用二分查找。

解题提示

  1. 答案可能是 0,也可能是数组长度 n
  2. 使用双闭区间时,若 nums[mid] 大于等于 target,就令 right = mid - 1,继续检查更靠左的位置;否则令 left = mid + 1
  3. 循环结束时 left 就是插入位置

LeetCode