AI summary
type
status
date
slug
summary
category
tags
icon
password
967. 连续差相同的数字
返回所有长度为n
且满足其每两个连续位上的数字之间的差的绝对值为k
的 非负整数 。请注意,除了 数字0
本身之外,答案中的每个数字都 不能 有前导零。例如,01
有一个前导零,所以是无效的;但0
是有效的。你可以按 任何顺序 返回答案。
其实可以直接套框架秒掉对吧,其实就是考察前文 回溯算法描述排列组合的 9 种变体 中的「元素无重可复选的排列」。
看出来了么?相当于给你
n
个盒子,然后你有 0~9 种球(元素)可以放进盒子,每个盒子只能放一个球,但每种球的数量无限,可以使用无数次。不过这道题比标准的「元素无重可复选的排列」多了两个剪枝逻辑:
- 第一个数字不能是 0,因为题目要求数字不能以 0 开头;
- 相邻两个数字的差的绝对值必须等于
k
。
你把前文「元素无重可复选的排列」的代码框架 copy 过来,把这两个剪枝逻辑加上就行了,两分钟搞定。
980. 不同路径 III
在二维网格grid
上,有 4 种类型的方格:
1
表示起始方格。且只有一个起始方格。
2
表示结束方格,且只有一个结束方格。
0
表示我们可以走过的空方格。
1
表示我们无法跨越的障碍。返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。
这道题不难,套回溯的框架直接出来了,真没啥可说的,我没太理解为啥这题难度是困难。
当然,可以通过记忆化搜索+状态压缩来优化。
用一个二进制数字 st 来表示路径还未经过的点(初始状态下为所有值为 0 的点和终点),点的坐标需要和二进制数字的位一一对应。定义函数 dp,输入参数为当前坐标 i,j 和未经过的点的二进制集合 st,返回值即为从点 (i,j) 出发,经过 st 代表的点的集合,最终到达终点的路径的条数。
由于暴力回溯已经 AC 了,懒得折腾了,代码略。
526. 优美的排列
假设有从 1 到 n 的 n 个整数。用这些整数构造一个数组perm
(下标从 1 开始),只要满足下述条件 之一 ,该数组就是一个 优美的排列 :
perm[i]
能够被i
整除
i
能够被perm[i]
整除给你一个整数n
,返回可以构造的 优美排列 的 数量 。
这个题就是标准的回溯算法。要么站在索引的视角,让每个索引来选择元素;要么站在元素的视角,让每个元素来选择要去的索引。
不过考虑到本题中,元素和索引差不多,都是
1~n
,所以这两种视角没有太大区别,我就写一个索引视角的回溯算法吧。这里是把 track 设为实例变量,比第一题中作为回溯函数的参数效率略高。
491. 非递减子序列
给你一个整数数组nums
,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
其实这道题就是前文讲过的「元素可重不可复选」的组合问题。
只不过这里又有一个额外的条件:组合中所有元素都必须是递增顺序。我们只需要添加一个剪枝条件即可。
另外,前文是利用排序的方式防止重复使用相同元素的,但这道题不能改变
nums
的原始顺序,所以不能用排序的方式,而是用 used
集合来避免重复使用相同元素。综上,只要把 40. 组合总和 II 的解法稍改一下即可完成这道题。
注意,这里的 used 集合是回溯函数内部的,表示减掉同一节点的相同孩子,不参与回溯过程。如下图所示:

131. 分割回文串
给你一个字符串s
,请你将s
分割成一些子串,使每个子串都是 回文串 。返回s
所有可能的分割方案。
我们就按照最直接粗暴的方式思考就行了:
从
s
的头部开始暴力穷举,如果发现 s[0..i]
是一个回文子串,那么我们就可以把 s
切分为 s[0..i]
和 s[i+1..]
,然后我们去尝试把 s[i+1..]
去暴力切分成多个回文子串即可。PS: 至于如何判断一个字符串是否是回文串,我在 数组双指针技巧汇总 中的左右指针部分有讲解,很简单。
把这个思路抽象成回溯树,树枝上是每次从头部穷举切分出的子串,节点上是待切分的剩余字符串:

只有树枝上的子串是回文串时才能继续往下走,最后如果能够走到空串节点,就说明整个
s
完成了切分,也就是得到了一个合法的答案。只要套用回溯算法框架,按照上述规则遍历整棵回溯树即可找到所有合法切分,直接看代码吧。
93. 复原 IP 地址
有效 IP 地址 正好由四个整数(每个整数位于0
到255
之间组成,且不能含有前导0
),整数之间用'.'
分隔。
- 例如:
"0.1.2.201"
和"192.168.1.1"
是 有效 IP 地址,但是"0.011.255.245"
、"192.168.1.312"
和"192.168@1.1"
是 无效 IP 地址。给定一个只包含数字的字符串s
,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在s
中插入'.'
来形成。你 不能 重新排序或删除s
中的任何数字。你可以按 任何 顺序返回答案。
做这道题之前我建议你先做一下 131. 分割回文串,因为稍微改改 131 题就可以解决这道题了。
131 题的要求是:让你把字符串
s
切分成若干个合法的回文串,返回所有的切分方法。本题的要求是:让你把字符串
s
切分成 4 个合法的 IP 数字,返回所有的切分方法。所以我们只要把 131 题的解法稍微改改,重写一个
isValid
函数判断合法的 IP 数字,并保证整棵回溯树只有 4 层(即 track
中只有 4 个子串)即可。具体看代码吧,基本逻辑和 131 题一模一样。
89. 格雷编码
n 位格雷码序列是一个由 个整数组成的序列,其中:
- 每个整数都在范围
[0, 2^n - 1]
内(含0
和2^n - 1
)
- 第一个整数是
0
- 一个整数在序列中出现 不超过一次
- 每对 相邻 整数的二进制表示 恰好一位不同 ,且
- 第一个 和 最后一个 整数的二进制表示 恰好一位不同
给你一个整数n
,返回任一有效的 n 位格雷码序列 。
不说数学相关的技巧,我们就用回溯算法框架暴力搜索,直接秒杀这道题。回溯树每个数字的子节点是对每个二进制位分别翻转得到的。
注意,题目只要求一个答案,所以找到答案后即可减掉剩下的所有树枝。
上面的暴力回溯算法 AC 地有些勉强,可以记一下公式:
二进制码→格雷码(编码):
此方法从对应的n位二进制码字中直接得到n位格雷码码字,步骤如下:
- 对n位二进制的码字,从右到左,以0到n-1编号
- 如果二进制码字的第 i 位和 i+1 位相同,则对应的格雷码的第 i 位为 0,否则为 1(当 i+1=n 时,二进制码字的第 n 位被认为是 0,即第 n-1 位不变)
17. 电话号码的字母组合
给定一个仅包含数字2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
还是套框架,甚至不用剪枝,没什么好说的,秒了:
79. 单词搜索
给定一个m x n
二维字符网格board
和一个字符串单词word
。如果word
存在于网格中,返回true
;否则,返回false
。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
这道题和 一文秒杀所有岛屿题目 中讲到的题目非常类似,递归暴力搜索就行了。
注意我们要对已经匹配过的字符做标记,比如用一个额外的
visited
布尔数组,或者使用其他方法标记 board
中已经匹配过的字符,比如改成小写,或者加上特殊符号。473. 火柴拼正方形
你将得到一个整数数组matchsticks
,其中matchsticks[i]
是第i
个火柴棒的长度。你要用 所有的火柴棍 拼成一个正方形。你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次 。如果你能拼成这个正方形,则返回true
,否则返回false
。
这道题,说到底就是问是否可以把数组
matchsticks
分成 4
个和相等的子集。那么这道题就可以直接秒了,我们调用
canPartitionKSubsets(matchsticks, 4)
即可计算本题的答案。