提示:努力生活,开心、快乐的一天
文章目录
216. 组合总和 III
💡解题思路
- 与之前组合题相似,只是此次集合变成固定的啦[1,9]
- 回溯三部曲:
和之前组合的题一样,先定义两个全局变量:result:存放结果集;path:符合条件的结果
- 确定递归函数参数:参数不用一上来直接确定好,可以在后面的代码编写中,缺什么再补,
已经确定的参数有:1、targetSum(int)目标和,也就是题目中的n。2、k(int)就是题目中要求k个数的集合。3、sum(int)为已经收集的元素的总和,也就是path里元素的总。4、startIndex(int)为下一层for循环搜索的起始位置。
let result = []
let path = []
let sum = 0
//startIndex :for循环遍历的起始位置
const dfs = (targetSum,k,sum,startIndex) => {
- 确定终止条件:k其实就已经限制树的深度,因为就取k个元素,树再往下深了没有意义,所以如果path.size() 和 k相等了,就终止。如果此时path里收集到的元素和(sum) 和targetSum(就是题目描述的n)相同了,就用result收集当前的结果。
if (path.length===k && sum === n) {
result.push([...path])
return
}
- 单层搜索过程:for循环+递归+回溯。集合固定的就是9个数[1,…,9],所以for循环固定i<=9,处理过程就是 path收集每次选取的元素,相当于树型结构里的边,sum来统计path里元素的总和。处理过程 和 回溯过程是一一对应的,处理有加,回溯就要有减
for (let i = startIndex; i <= 9 ; i++){
sum += i //path里元素的总和
path.push(i)//收集选取的元素
dfs(i + 1)//递归,深一层
sum -= i//回溯
path.pop()//回溯
}
🤔遇到的问题
- 终止条件遗漏啦。终止条件有2个:1、当前集合的个数=k;2、当前集合的和=n
- for循环时,遍历的集合区间是左闭右闭,所以,i<=9
💻代码实现
回溯-包含剪枝
var combinationSum3 = function(k, n) {
let result = []
let path = []
let sum = 0
//startIndex :for循环遍历的起始位置
const dfs = (startIndex) => {
// 剪枝操作
if (sum > n) return
//终止条件有2个:1、当前集合的个数=k;2、当前集合的和=n
if (path.length===k && sum === n) {
result.push([...path])
return
}
for (let i = startIndex; i <= 9 - (k - path.length) + 1; i++){
sum += i //path里元素的总和
path.push(i)//收集选取的元素
dfs(i + 1)//递归,深一层
sum -= i//回溯
path.pop()//回溯
}
}
dfs(1)
return result
};
🎯题目总结
本题有2个地方可以做剪枝:
1、已选元素总和如果已经大于n了,那么往后遍历就没有意义了,直接剪掉,那么剪枝的地方可以放在递归函数开始的地方,剪枝代码如下:
if (sum > n) return
2、 for循环的范围也可以剪枝,i <= 9 - (k - path.size()) + 1就可以了
17. 电话号码的字母组合
💡解题思路
- 要解决如下三个问题:
问题一:数字和字母如何映射
问题二:两个字母就两个for循环,三个字符我就三个for循环,以此类推,然后发现代码根本写不出来
问题三:输入1 * #按键等等异常情况 - 解决上面问题:
问题一的解决:定义一个数组,const map = [“”, “”, “abc”, “def”, “ghi”, “jkl”, “mno”, “pqrs”, “tuv”, “wxyz”];
问题二的解决:回溯法来解决n个for循环的问题
问题三的解决:题目的测试数据中应该没有异常情况的数据,如果是现场面试中,一定要考虑到! - 回溯三部曲:
- 确定回溯函数参数:定义两个全局变量,result保存结果集,str收集叶子节点的结果。参数指定是有题目中给的digits,然后还要有一个参数就是index,index是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index也表示树的深度
- 确定终止条件:终止条件就是如果index 等于 输入的数字个数(digits.size)了(本来index就是用来遍历digits的),然后收集结果,结束本层递归。
- 确定单层遍历逻辑:首先要取index指向的数字,并找到对应的字符集,然后for循环来处理这个字符集
🤔遇到的问题
- 输入的字符串为空获知只有一个数字的特殊操作可以单独写
💻代码实现
回溯
var letterCombinations = function (digits) {
let result = []// 存放符合条件结果的集合
let str = []// 用来存放符合条件结果
//字符串中数字与字母的对应map
const map = ["", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"];
//字符串的长度为空
if (!digits.length) return []
//字符串的长度为1
if (digits.length === 1) return map[digits].split('')
//index:字符串的下标,记录遍历第几个数字,也表示树的深度。
const backtracking = (digits, index) => {
//终止条件:index 等于 输入的数字个数(digits.size)了,收集结果,结束本层递归
if (index === digits.length) {
result.push(str.join(''))
return
}
//要取index指向的数字,并找到对应的字符集
//然后for循环来处理这个字符集
for (const item of map[digits[index]]) {
str.push(item)// 处理
backtracking(digits, index + 1) // 递归,注意index+1,一下层要处理下一个数字了
str.pop()// 回溯
}
}
backtracking(digits, 0)
return result
};
🎯题目总结
多个集合求组合,需要画图,确定集合的宽度及树的深度,也就是for循环的条件和遍历的深度
🎈今日心得
电话号码的题确实不容易想到