回溯算法
解题过程=决策树的遍历过程:
- 路径: 已经做出的选择
- 选择列表: 当前可以做的选择
- 结束条件: 到达决策树底层, 无法再做选择的条件(可选列表为空等)
回溯算法代码框架:
1 | result = [] |
时间复杂度分析:
递归相关的算法, 时间复杂度计算为: [递归次数]*[递归本身的时间复杂度].
经典例题
N皇后问题
[51] N-Queens
套用回溯框架即可, 每确定一个皇后位置, 将其加入已经选择的路径中, 接着进行选择.
学了框架之后, 思路都清楚了很多.
1 | // 皇后彼此不能相互攻击也就是说:任何两个皇后都不能处于同一条横行、纵行或斜线上 |
子集问题
[78] Subsets
Given a set of distinct integers, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
观察全排列/组合/子集问题,它们比较相似,且可以使用一些通用策略解决。
首先,它们的解空间非常大:
- 全排列:N!。
- 组合:N!。
- 子集:2^N, 每个元素都可能存在或不存在。
在它们的指数级解法中,要确保生成的结果 完整 且 无冗余,有三种常用的方法:
- 递归
- 回溯
- 基于二进制位掩码和对应位掩码之间的映射字典生成排列/组合/子集
相比前两种方法,第三种方法将每种情况都简化为二进制数,易于实现和验证。
此外,第三种方法具有最优的时间复杂度,可以生成按照字典顺序的输出结果。
solution 1 : 字典排序(二进制排序) 子集
这种解法很巧妙, 由于是全排列问题, 子集的数量与数组长度有关.
数组中的元素, 每个只有在或者不在子集中这两种选择. 对于每一种可能, 都能用二进制来标记.
因此该方法的思路如下:
假设数组为[1, 2, 4], 则子集数量为 2^len= 2^3 = 8
则从 0 - 7 的二进制对应分别为 000-111
每一种可能都对应一种子集详情, 比如 101 对应 [1,4], 001 对应 [4].
需要注意的点是, 在将十进制转换为二进制时, 需要将二进制的位数扩充至与nums的长度相等.
1 | var subsets = function (nums) { |
复杂度分析
- 时间复杂度:O(N×2^N),生成所有的子集,并复制到输出列表中。
- 空间复杂度:O(N×2^N),存储所有子集,共 n 个元素,每个元素都有可能存在或者不存在。
solution 2 : 数学归纳法
这个解法挺巧妙的, 每次都把新元素加进已有的所有子集, 生成新的子集, 因为每个元素只有在和不在两种情况.
1 | var subsets = function (nums) { |
时间复杂度: 总共添加2^N个子集, 每个子集是数组形式, 子集通过深拷贝塞进结果数组中, 拷贝耗时O(N). 因此, 总的时间复杂度是 O(N*2^N).
空间复杂度: 存储每个子集需要 O(N)的递归堆栈空间(不算结果集), 算上结果的话, 一共2^N个子集, 因此所需空间为 O(N*2^N).
Solution 3 : 回溯解法
使用回溯模板. 控制start.
1 | var subsets = (nums) => { |
[90] Subsets II
Given a collection of integers that might contain duplicates, nums\, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
思路与子集时类似, 只是需要剪枝操作, 将同一层其余相同的元素除去.
1 | var subsetsWithDup = function (nums) { |
括号问题
两个性质:
- 合法的括号组合中, 左括号数目=右括号数目.
- 一个合法的括号组合字符串 s, 对于任意 0<=i
=右括号数目.
[22] Generate Parentheses
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
对于该题题意的理解, 可以是, 现在有 2n 个位置, 每个位置可以放置'('(数目为n)/')'(数目为n), 请问一共有多少种合法的放置方式?
则解题思路是: 先得到 2^(2n) 种组合方式, 再去除不合法的部分即可.
1 | // 2020/10/21 学习回溯模板之后 很简洁 |
排列问题
[46] Permutations
Given a collection of distinct integers, return all possible permutations.
时间复杂度: O(N*N!)
全排列一共有 N! 种可能, 实现每一种可能需要遍历整个数组即O(N)时间, 所以可得.
使用回溯模板. 删除已经选择的数字.
1 | var permute = (nums) => { |
组合问题
[77] Combinations
Given two integers n and k, return all possible combinations of k numbers out of 1 … n.
You may return the answer in any order.
使用回溯模板, 决策树的深度为 k. 将深度为k的节点均加入节点列表.
其实就是自己问题的一部分, 使用start排除已经做出的选择.
1 | var combine = (n, k) => { |
其他问题
[79] Word Search
Given a 2D board and a word, find if the word exists in the grid.
The word can be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once.
回溯算法本质就是暴力穷举.
1 | var exist = function (board, word) { |
[93] Restore IP Addresses
Given a string
scontaining only digits. Return all possible valid IP addresses that can be obtained froms. You can return them in any order.A valid IP address consists of exactly four integers, each integer is between
0and255, separated by single points and cannot have leading zeros. For example, “0.1.2.201” and “192.168.1.1” are valid IP addresses and “0.011.255.245”, “192.168.1.312” and “192.168@1.1” are invalid IP addresses.
就是大概有想法但是却实现不出来, 说明思路还不是很清楚, 需要画树状图帮助自己理解.
多画树状图, 理解回溯过程. 被这道题折磨好久.
1 | /** |
[473] Matchsticks to Square
Remember the story of Little Match Girl? By now, you know exactly what matchsticks the little match girl has, please find out a way you can make one square by using up all those matchsticks. You should not break any stick, but you can link them up, and each matchstick must be used exactly one time.
Your input will be several matchsticks the girl has, represented with their stick length. Your output will either be true or false, to represent whether you could make one square using all the matchsticks the little match girl has.
需要注意的是, 在采用递归方法的情况下, 这里不需要在选择位置的外面再套一层for循环去遍历火柴, 因为不存在第一根火柴找到合适位置之后, 把第一根火柴去掉, 以第二根火柴为第一根火柴, 再重新开始摆放. 只需要每根火柴摆放好之后, 去递归摆放下一根火柴即可.
1 | // 使用小女孩的所有火柴拼成一个正方形 |