递归法
递归法
22 括号生成
题目描述:数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
思路:这道题可以用递归+回溯的方法,满足( 的数量不能大于n,( 数量比 )多的时候才能加右( 。string长度等于2n的时候就递归到头了。优先向左括号增多的方向递归。
backTracking = func(s *[]string, cur string, left int, right int, max int) {
if len(cur) == max*2 { // 递归到头了
*s = append(*s, cur)
}
// 这个遍历顺序类似于一个中序遍历,也满足了字典顺序,即把( 看作1,)看作0,总是以一个较大的值排列
// 左括号数量不能大于n
if left < max {
cur += "("
backTracking(s, cur, left+1, right, max)
cur = cur[:len(cur)-1]
}
// 左括号数量要大于右括号数量,才能网上加右括号
if left > right {
cur += ")"
backTracking(s, cur, left, right+1, max)
cur = cur[:len(cur)-1]
}
}
backTracking(&res, "", 0, 0, n)
return res
}
491 递增子序列
给你一个整数数组 nums
,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
输入: nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]
注意点:
同一层相同的值不能重复使用,所以要进行去重
这题跨层一定不能得出重复的值,所以跨层不需要管
注意递归下标
赋值 res 之前先复制
回溯不能删除标记
二维数组传递指针,不要使用全局变量的形式!
func findSubsequences(nums []int) [][]int {
res := make([][]int, 0)
tmp := make([]int, 0)
backTracking(nums, tmp, 0, &res)
return res
}
// res: 暂时生成的递归子序列; idx: 开始遍历nums数组中下标为idx的元素, nums: 输入数组;
// tmp: 生成的中间结果的数组;
func backTracking(nums, tmp []int, idx int, res *[][]int) {
if len(tmp) >= 2 { // 子序列长度满足要求
t := make([]int, len(tmp))
copy(t, tmp)
*res = append(*res, t)
}
// 设置 used 数组,防止同父节点的同层节点相同的值重复使用!
history := make(map[int]struct{})
for i := idx; i < len(nums); i++ {
// 防止重复使用
_, exist := history[nums[i]]
if exist {
continue
}
if len(tmp) == 0 || (len(tmp) > 0 && nums[i] >= tmp[len(tmp)-1]) {
// nums 元素递增且没有出现过
tmp = append(tmp, nums[i]) // 加入
history[nums[i]] = struct{}{} // 加入标记
/**
-------------- bug 记录 ----------------
backTracking(nums, tmp, idx+1, res)
下一层应该从遍历到的子节点的下一个开始,而不是从父节点的下一个开始!!!!
*/
backTracking(nums, tmp, i+1, res) // 递归
tmp = tmp[:len(tmp)-1] // 回溯
/**
-------------- bug 记录 ----------------
delete(history, (*nums)[i]) 不能删除标记,因为标记的作用就是在当前层中不要找值相等的元素
*/
}
}
}
40 组合总和 II
给定一个候选人编号的集合 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用 一次 。
注意: 解集不能包含重复的组合。
输入: candidates = [10,1,2,7,6,1,5]
, target = 8
,
输出: [[1,1,6],[1,2,5],[1,7],[2,6]]
注意点:
题中已经明确解集不能包含重复的组合,所以必须进行跨层去重!
既然跨层需要去重,那么同层必然也需要去重,相同值的子节点不能重复加入。
跨层去重采取 map[string] 来去重,将数组 排序+输出 为字符串形式,然后进行去重。
同层去重就使用 map[int] 即可。
注意看数组是怎么输出为字符串形式的。
// 递归+回溯求解
func combinationSum2(candidates []int, target int) [][]int {
res := make([][]int, 0)
subRes := make([]int, 0)
history := make(map[string]struct{}) // 跨层不能得出相同的值
backTracking_comb(subRes, 0, &res, history, candidates, target)
return res
}
func backTracking_comb(subRes []int, idx int, res *[][]int, history map[string]struct{}, candidates []int, target int) {
sum := 0
for i := 0; i < len(subRes); i++ {
sum += subRes[i]
}
if sum == target {
tmp := make([]int, len(subRes))
copy(tmp, subRes)
// 排序
sort.Slice(tmp, func(i, j int) bool {
return tmp[i] < tmp[j]
})
// 编码
s := encode(tmp)
// 查看是否存在
if _, exists := history[s]; !exists {
history[s] = struct{}{}
*res = append(*res, tmp)
}
}
subHistory := make(map[int]struct{}) // 同一层不能得出相同的值
for i := idx; i < len(candidates); i++ {
if _, exists := subHistory[candidates[i]]; exists {
continue
}
if candidates[i]+sum > target { // 加入之后超限了
continue
}
subRes = append(subRes, candidates[i])
subHistory[candidates[i]] = struct{}{}
backTracking_comb(subRes, i+1, res, history, candidates, target)
subRes = subRes[:len(subRes)-1]
}
}
func encode(tmp []int) string {
return fmt.Sprintf("%v", tmp)
}
131. 分割回文串
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
输入: s = "aab"
输出: [["a","a","b"],["aa","b"]]
注意点:
同层和跨层都不会产生相同的结果,所以不需要进行去重
重点在划分的时候,边界怎么控制的,这里是 idx 为边界,从 idx 开始划分,idx 是闭区间的。一直划分到 i,这里 i 也是闭区间的,因此 i <= length-1。然后下次递归从 i + 1 开始划分。
idx 如果等于 length,说明划分结束了,直接加入结果即可。
func partition(s string) [][]string {
res := make([][]string, 0)
subRes := make([]string, 0)
backTracking_part(subRes, 0, &res, []byte(s))
return res
}
// idx: 从idx开始分割,idx处的元素未分割,idx最后的值为 (length - 1) + 1
func backTracking_part(subRes []string, idx int, res *[][]string, originalStr []byte) {
if idx == len(originalStr) {
tmp := make([]string, len(subRes))
copy(tmp, subRes)
// 跨层应该也不能产生相同的结果
*res = append(*res, tmp)
}
// 同层不可能产生相同结果
for i := idx; i < len(originalStr); i++ {
if !judge(originalStr[idx : i+1]) { // [idx, i] (i <= length-1)
continue
}
subRes = append(subRes, string(originalStr[idx:i+1]))
backTracking_part(subRes, i+1, res, originalStr)
subRes = subRes[:len(subRes)-1]
}
}
func judge(s []byte) bool {
if len(s) == 1 {
return true
}
if len(s) == 0 {
return false
}
i := 0
j := len(s) - 1
for i < j {
if s[i] != s[j] {
return false
}
i++
j--
}
return true
}