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

第 97 节 · SECTION 97

练习

知识巩固

这段排列算法会漏掉结果吗

一个回溯算法按 1、2、3 的顺序尝试生成全部排列。每次选择数字 x 时,它会:

  1. 把 x 加到当前路径末尾;
  2. 把 x 标记为“已使用”;
  3. 递归填写下一个位置。

递归返回后,同学只从路径末尾删除了 x,然后继续尝试下一个数字。

  1. 算法首先得到哪个排列?它还能得到全部 6 个排列吗?
  2. 递归返回上一层前,只删除路径末尾的数字是否足够?如果不够,还需要做什么?说明理由。

参考答案

  1. 它首先得到 [1, 2, 3],但无法得到全部排列。虽然返回时路径变短了, 数字 1、2、3 的标记仍都是“已使用”,后续分支便没有可选数字。

  2. 不够。删除路径末尾的 x 后,还必须把 x 重新标记为“未使用”。 当前路径和已使用标记共同描述搜索状态;选择时修改了两处,回退时也必须把两处都恢复, 其他分支才能再次选择 x。

数字的选择顺序重要吗

给定排好序的数组 [2, 3, 5] 和目标值 5,每个数可以重复选择。 算法规定每条搜索路径中的数字只能按从小到大的顺序出现。

  1. 能得到哪些不同的组合?
  2. 为什么同一组数字不需要按不同顺序重复搜索?“从小到大”的限制起到了什么作用?
  3. 当前路径为 [3]、还差 2 时,下一个候选数是 3。为什么此时可以停止检查这一层后面的所有候选数?

参考答案

  1. 不同的组合为 [2, 3] 和 [5]。

  2. 本题把 [2, 3] 和 [3, 2] 看作同一种组合,数字的选择顺序不计入答案。 规定路径中的数字从小到大出现,就能在搜索时直接跳过 [3, 2] 这类重复组合。

  3. 当前还差 2,而候选数 3 已经大于 2。因为数组已经排好序, 3 后面的候选数只会更大,也都不可能加入当前组合,所以可以直接结束这一层的检查。

下一枚皇后可以放在哪些位置

在一个 4 × 4 棋盘上按行放置皇后,行、列下标都从 0 开始。 目前已经在 (0, 1) 和 (1, 3) 放置了皇后,现在要在第 2 行放置下一个皇后。

  1. 哪些列会因为“同列”而被排除?
  2. 在剩余列中,哪些位置会因为“同一条对角线”而被排除?
  3. 第 2 行还剩哪些位置可以尝试?

参考答案

  1. 第 1 列和第 3 列已经有皇后,因此位置 (2, 1) 和 (2, 3) 被排除。

  2. 在剩余位置中,(2, 2) 与 (1, 3) 位于同一条对角线上,因此也被排除。 位置 (2, 0) 与已有两个皇后都不在同列或同一条对角线上。

  3. 第 2 行可以尝试的位置只有 (2, 0)。

    这一步只说明当前放置合法;之后若无法完成棋盘,仍需回退并尝试更早的其他选择。

编程练习

无重复元素的全排列

整数数组 nums 至少包含一个元素,并且其中各元素互不相同。 请列出把这些元素各使用一次所能形成的全部顺序,并将每一种顺序作为一个数组返回。 结果中各排列的先后次序不作要求。 请使用回溯,并用布尔数组记录每个位置的元素是否已经选入当前排列。

解题提示

  1. 递归深度表示正在填写排列中的第几个位置
  2. 每一层只尝试尚未使用的元素
  3. 路径长度达到 nums 的长度时,把它的副本加入答案

LeetCode 题目解析

说明: 链接题解通过交换数组元素,把已选元素依次放到数组前部;本练习使用布尔数组记录每个元素是否已选。两种方法都能避免同一元素被重复选择,但代码结构不同