diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md index dd820a39..12a45a61 100644 --- a/problems/0017.电话号码的字母组合.md +++ b/problems/0017.电话号码的字母组合.md @@ -120,7 +120,7 @@ for (int i = 0; i < letters.size(); i++) { **注意这里for循环,可不像是在[回溯算法:求组合问题!](https://programmercarl.com/0077.组合.html)和[回溯算法:求组合总和!](https://programmercarl.com/0216.组合总和III.html)中从startIndex开始遍历的**。 -**因为本题每一个数字代表的是不同集合,也就是求不同集合之间的组合,而[77. 组合](https://programmercarl.com/0077.组合.html)和[216.组合总和III](https://programmercarl.com/0216.组合总和III.html)都是是求同一个集合中的组合!** +**因为本题每一个数字代表的是不同集合,也就是求不同集合之间的组合,而[77. 组合](https://programmercarl.com/0077.组合.html)和[216.组合总和III](https://programmercarl.com/0216.组合总和III.html)都是求同一个集合中的组合!** 注意:输入1 * #按键等等异常情况 @@ -356,38 +356,32 @@ class Solution: 主要在于递归中传递下一个数字 ```go +var ( + m []string + path []byte + res []string +) func letterCombinations(digits string) []string { - lenth:=len(digits) - if lenth==0 ||lenth>4{ - return nil + m = []string{"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"} + path, res = make([]byte, 0), make([]string, 0) + if digits == "" { + return res } - digitsMap:= [10]string{ - "", // 0 - "", // 1 - "abc", // 2 - "def", // 3 - "ghi", // 4 - "jkl", // 5 - "mno", // 6 - "pqrs", // 7 - "tuv", // 8 - "wxyz", // 9 - } - res:=make([]string,0) - recursion("",digits,0,digitsMap,&res) - return res + dfs(digits, 0) + return res } -func recursion(tempString ,digits string, Index int,digitsMap [10]string, res *[]string) {//index表示第几个数字 - if len(tempString)==len(digits){//终止条件,字符串长度等于digits的长度 - *res=append(*res,tempString) +func dfs(digits string, start int) { + if len(path) == len(digits) { //终止条件,字符串长度等于digits的长度 + tmp := string(path) + res = append(res, tmp) return } - tmpK:=digits[Index]-'0' // 将index指向的数字转为int(确定下一个数字) - letter:=digitsMap[tmpK]// 取数字对应的字符集 - for i:=0;i path; // 用来存放符合条件结果 其实不定义这两个全局变量也是可以的,把这两个变量放进递归函数的参数里,但函数里参数太多影响可读性,所以我定义全局变量了。 -函数里一定有两个参数,既然是集合n里面取k的数,那么n和k是两个int型的参数。 +函数里一定有两个参数,既然是集合n里面取k个数,那么n和k是两个int型的参数。 然后还需要一个参数,为int型变量startIndex,这个参数用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,...,n] )。 @@ -389,9 +389,8 @@ class Solution(object): # 剪枝, 最后k - len(path)个节点直接构造结果,无需递归 last_startidx = n - (k - len(path)) + 1 - result.append(path + [idx for idx in range(last_startidx, n + 1)]) - for x in range(startidx, last_startidx): + for x in range(startidx, last_startidx + 1): path.append(x) backtracking(n, k, x + 1) # 递归 path.pop() # 回溯 @@ -435,6 +434,36 @@ class Solution: return res ``` +### Go +```Go +var ( + path []int + res [][]int +) + +func combine(n int, k int) [][]int { + path, res = make([]int, 0, k), make([][]int, 0) + dfs(n, k, 1) + return res +} + +func dfs(n int, k int, start int) { + if len(path) == k { // 说明已经满足了k个数的要求 + tmp := make([]int, k) + copy(tmp, path) + res = append(res, tmp) + return + } + for i := start; i <= n; i++ { // 从start开始,不往回走,避免出现重复组合 + if n - i + 1 < k - len(path) { // 剪枝 + break + } + path = append(path, i) + dfs(n, k, i+1) + path = path[:len(path)-1] + } +} +``` ### javascript @@ -481,63 +510,6 @@ function combine(n: number, k: number): number[][] { }; ``` - - -### Go -```Go -var res [][]int -func combine(n int, k int) [][]int { - res=[][]int{} - if n <= 0 || k <= 0 || k > n { - return res - } - backtrack(n, k, 1, []int{}) - return res -} -func backtrack(n,k,start int,track []int){ - if len(track)==k{ - temp:=make([]int,k) - copy(temp,track) - res=append(res,temp) - } - if len(track)+n-start+1 < k { - return - } - for i:=start;i<=n;i++{ - track=append(track,i) - backtrack(n,k,i+1,track) - track=track[:len(track)-1] - } -} -``` -剪枝: -```Go -var res [][]int -func combine(n int, k int) [][]int { - res=[][]int{} - if n <= 0 || k <= 0 || k > n { - return res - } - backtrack(n, k, 1, []int{}) - return res -} -func backtrack(n,k,start int,track []int){ - if len(track)==k{ - temp:=make([]int,k) - copy(temp,track) - res=append(res,temp) - } - if len(track)+n-start+1 < k { - return - } - for i:=start;i<=n;i++{ - track=append(track,i) - backtrack(n,k,i+1,track) - track=track[:len(track)-1] - } -} -``` - ### Rust ```Rust diff --git a/problems/0077.组合优化.md b/problems/0077.组合优化.md index 91c7ce3b..34318d16 100644 --- a/problems/0077.组合优化.md +++ b/problems/0077.组合优化.md @@ -133,7 +133,7 @@ public: # 总结 -本篇我们准对求组合问题的回溯法代码做了剪枝优化,这个优化如果不画图的话,其实不好理解,也不好讲清楚。 +本篇我们针对求组合问题的回溯法代码做了剪枝优化,这个优化如果不画图的话,其实不好理解,也不好讲清楚。 所以我依然是把整个回溯过程抽象为一棵树形结构,然后可以直观的看出,剪枝究竟是剪的哪里。 @@ -194,28 +194,28 @@ class Solution: ``` Go: ```Go -var res [][]int +var ( + path []int + res [][]int +) + func combine(n int, k int) [][]int { - res=[][]int{} - if n <= 0 || k <= 0 || k > n { - return res - } - backtrack(n, k, 1, []int{}) - return res + path, res = make([]int, 0, k), make([][]int, 0) + dfs(n, k, 1) + return res } -func backtrack(n,k,start int,track []int){ - if len(track)==k{ - temp:=make([]int,k) - copy(temp,track) - res=append(res,temp) + +func dfs(n int, k int, start int) { + if len(path) == k { + tmp := make([]int, k) + copy(tmp, path) + res = append(res, tmp) + return } - if len(track)+n-start+1 < k { - return - } - for i:=start;i<=n;i++{ - track=append(track,i) - backtrack(n,k,i+1,track) - track=track[:len(track)-1] + for i := start; i <= n - (k-len(path)) + 1; i++ { + path = append(path, i) + dfs(n, k, i+1) + path = path[:len(path)-1] } } ``` diff --git a/problems/0216.组合总和III.md b/problems/0216.组合总和III.md index 9fc949cc..19fe1eb9 100644 --- a/problems/0216.组合总和III.md +++ b/problems/0216.组合总和III.md @@ -36,7 +36,7 @@ 想到这一点了,做过[77. 组合](https://programmercarl.com/0077.组合.html)之后,本题是简单一些了。 -本题k相当于了树的深度,9(因为整个集合就是9个数)就是树的宽度。 +本题k相当于树的深度,9(因为整个集合就是9个数)就是树的宽度。 例如 k = 2,n = 4的话,就是在集合[1,2,3,4,5,6,7,8,9]中求 k(个数) = 2, n(和) = 4的组合。 @@ -380,29 +380,32 @@ class Solution: 回溯+减枝 ```go +var ( + res [][]int + path []int +) func combinationSum3(k int, n int) [][]int { - var track []int// 遍历路径 - var result [][]int// 存放结果集 - backTree(n,k,1,&track,&result) - return result + res, path = make([][]int, 0), make([]int, 0, k) + dfs(k, n, 1, 0) + return res } -func backTree(n,k,startIndex int,track *[]int,result *[][]int){ - if len(*track)==k{ - var sum int - tmp:=make([]int,k) - for k,v:=range *track{ - sum+=v - tmp[k]=v - } - if sum==n{ - *result=append(*result,tmp) + +func dfs(k, n int, start int, sum int) { + if len(path) == k { + if sum == n { + tmp := make([]int, k) + copy(tmp, path) + res = append(res, tmp) } return } - for i:=startIndex;i<=9-(k-len(*track))+1;i++{//减枝(k-len(*track)表示还剩多少个可填充的元素) - *track=append(*track,i)//记录路径 - backTree(n,k,i+1,track,result)//递归 - *track=(*track)[:len(*track)-1]//回溯 + for i := start; i <= 9; i++ { + if sum + i > n || 9-i+1 < k-len(path) { + break + } + path = append(path, i) + dfs(k, n, i+1, sum+i) + path = path[:len(path)-1] } } ``` diff --git a/problems/0235.二叉搜索树的最近公共祖先.md b/problems/0235.二叉搜索树的最近公共祖先.md index 391d1cfb..7f8f6422 100644 --- a/problems/0235.二叉搜索树的最近公共祖先.md +++ b/problems/0235.二叉搜索树的最近公共祖先.md @@ -60,12 +60,12 @@ 而递归遍历顺序,本题就不涉及到 前中后序了(这里没有中节点的处理逻辑,遍历顺序无所谓了)。 -如图所示:p为节点3,q为节点5 +如图所示:p为节点6,q为节点9 ![235.二叉搜索树的最近公共祖先2](https://code-thinking-1253855093.file.myqcloud.com/pics/20220926165141.png) -可以看出直接按照指定的方向,就可以找到节点4,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回! +可以看出直接按照指定的方向,就可以找到节点8,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回! ## 递归法