回溯
存在模板:
dfs = func(ele... interface{}) {
if ele == len { // 递归结束条件
return
}
// 考虑当前位置
set = append(set, ele)
dfs(ele + 1) // 递归下一个位置
set = set[: len(set) - 1] // 不考虑当前位置
dfs(ele + 1) // 递归下一个位置
}
Subset
78. 子集
// mine
func subsets(nums []int) [][]int {
res := make([][]int , 0)
res = append(res, []int{})
for _, num := range nums {
length := len(res)
for i:=0; i<length; i++ {
tmp := make([]int, len(res[i]))
copy(tmp, res[i])
res = append(res, append(tmp, num))
}
}
return res
}
// 2021-02-19
func subsets(nums []int) [][]int {
res := make([][]int, 0)
set := make([]int, 0)
var dfs func(index int)
dfs = func(index int) {
if index == len(nums) {
res = append(res, append([]int{}, set...))
return
}
set = append(set, nums[index])
dfs(index + 1)
set = set[: len(set) - 1] // 减少一个数进行遍历
dfs(index + 1)
}
dfs(0)
return res
}
// others
func subsets(nums []int) (ans [][]int) {
set := []int{}
var dfs func(int)
dfs = func(cur int) {
if cur == len(nums) {
ans = append(ans, append([]int(nil), set...))
// note this, must use append([]int(nil), set...)
// else error happen, because 这个参数传进来是一个引用,后面会不断修改,导致出错误
return
}
set = append(set, nums[cur])
dfs(cur + 1)
set = set[:len(set)-1]
dfs(cur + 1)
}
dfs(0)
return
}
Subset
90. 子集 II
func subsetsWithDup(nums []int) [][]int {
res := make([][]int,0)
sort.Ints(nums)
dfs(&res, nums, []int(nil), 0)
return res
}
func dfs(res *[][]int, nums, set []int, cur int) {
// fmt.Println(set)
*res = append(*res, append([]int{},set...))
for i:=cur; i<len(nums); i++ {
if i>cur && nums[i] == nums[i-1] {
continue
}
set = append(set, nums[i])
dfs(res, nums, set, i+1)
set = set[:len(set) - 1]
}
}
//Interge form
func subsetsWithDup(nums []int) [][]int {
res := make([][]int,0)
sort.Ints(nums)
length := len(nums)
set := []int{}
var dfs func(int)
dfs = func(cur int) {
// res = append(res, append([]int(nil),set...))
res = append(res, append([]int(nil), set...))
for i:=cur; i<length; i++ {
if i>cur && nums[i] == nums[i-1] {
continue
}
set = append(set, nums[i])
dfs(i+1)
set = set[:len(set) - 1]
}
}
dfs(0)
return res
}
// 2021-02-19 模板类
func subsetsWithDup(nums []int) [][]int {
sort.Ints(nums)
res := make([][]int, 0)
set := make([]int, 0)
length := len(nums)
var dfs func(index int)
dfs = func(index int) {
if index == length {
res = append(res, append([]int{}, set...))
return
}
set = append(set, nums[index])
dfs(index + 1)
cur := nums[index]
for index < length && cur == nums[index] { // 去掉重复的数字不再考虑
index++
}
set = set[:len(set) - 1]
dfs(index)
}
dfs(0)
return res
}
Combination Sum
39. 组合总和
combination 系列,感觉这里是一个树,画出一个树的形式,确定要选哪一种情况,这个题就是,情况一:如果选当前一个candidate,后面是可以继续选的;情况二:不选这个的话,就跳过这个candidate,找它下一个candidate。这个就是相当于一棵二叉树啦
func combinationSum(candidates []int, target int) [][]int {
set := []int{}
res := make([][]int, 0)
var dfs func(setSum, cur int)
dfs = func(setSum, cur int) {
// fmt.Println(setSum, cur)
if cur == len(candidates) {
return
}
if setSum == target {
res = append(res, append([]int{}, set...))
return
}
dfs(setSum,cur+1) //跳过这个数字
if setSum + candidates[cur] <= target { //用这个数字,同时下一次可以继续用
set = append(set, candidates[cur])
dfs(setSum+candidates[cur], cur)
set = set[:len(set)-1]
}
}
dfs(0,0)
return res
}
// 2021-02-19 模板
func combinationSum(candidates []int, target int) [][]int {
sort.Ints(candidates)
res := make([][]int, 0)
set := make([]int, 0)
var dfs func(index, sum int)
dfs = func(index, sum int) {
if sum == target {
res = append(res, append([]int{}, set...))
return
}
if index == len(candidates) {
return
}
tmp := sum + candidates[index]
set = append(set, candidates[index])
if tmp <= target {
dfs(index, tmp) // 考虑当前元素,而且可以重复考虑
}
set = set[: len(set) - 1]
dfs(index + 1, sum)
}
dfs(0, 0)
return res
}
Combination Sum2
40. 组合总和 II
这个combinationSum2实际上也是一个二叉树,对于一个candidate,要么选,要么不选,不过需要注意的话,如果不选这个candidate,需要判断其后又没有跟他一样的数字,因此这个题需要先对数字排序,然后去掉重复的candidate;如果选这个candidate的话,因为只能选一次,因此需要指向下一个candidate了
func combinationSum2(candidates []int, target int) [][]int {
res := make([][]int,0)
set := make([]int,0)
sort.Ints(candidates) // 排序
dfs(&res, set, candidates, 0, 0, target)
return res
}
func dfs(res *[][]int, set, candidates []int, setSum, cur, target int) {
// fmt.Println(set, setSum, cur)
if cur == len(candidates) {
return
}
next := cur+1 //不选的时候,去掉相同candidate
for ; next<len(candidates); next++{
if candidates[next] != candidates[cur] {
break
}
}
dfs(res, set, candidates, setSum, next, target) // 遍历与当前candidate后面第一不同的数字
if setSum + candidates[cur] < target { //如果小于target,证明还可以加,可以继续找后面的数字
set = append(set, candidates[cur])
dfs(res,set,candidates,setSum + candidates[cur],cur+1,target)
} else if setSum + candidates[cur] == target {//如果等于,就加上去res了,就不再进行寻找了,因为是有排序过的,再加上去会大于target,没有意义。
set = append(set, candidates[cur])
*res = append(*res, append([]int{}, set...))
// dfs(res,set,candidates,setSum + candidates[cur],cur+1,target)
}
// 2021-02-19
func combinationSum2(candidates []int, target int) [][]int {
sort.Ints(candidates)
res := make([][]int, 0)
set := make([]int, 0)
length := len(candidates)
var dfs func(index, sum int)
dfs = func(index, sum int) {
if sum == target {
res = append(res, append([]int{}, set...))
return
}
if index == length {
return
}
tmp := sum + candidates[index]
set = append(set, candidates[index])
if tmp <= target {
dfs(index + 1, tmp) // 考虑当前元素,而且不可以重复考虑
}
curNum := candidates[index]
for index < length && curNum == candidates[index] { //去掉重复的元素
index++
}
set = set[: len(set) - 1]
dfs(index, sum) // 已经自加了
}
dfs(0, 0)
return res
}
combinationSum3
func combinationSum3(k int, n int) [][]int {
res := make([][]int, 0)
if k > n || n > 45 {
return res
}
set := make([]int, 0)
var dfs func(cur, setSum int)
dfs = func(cur, setSum int) {
// fmt.Println(cur, set)
if cur == 10 || setSum > n || len(set) > k{
return
}
dfs(cur+1, setSum)
// xuan
set = append(set, cur)
setSum += cur
if len(set) == k && setSum == n {
res = append(res, append([]int{}, set...))
} else {
dfs(cur+1,setSum)
}
set = set[:len(set)-1]
}
dfs(1,0)
return res
}
// 2021-02-19
func combinationSum3(k int, n int) [][]int {
res := make([][]int, 0)
set := make([]int, 0)
var dfs func(index, sum int)
dfs = func(index, sum int) {
if sum == n && len(set) == k {
res = append(res, append([]int{}, set...))
return
}
if index == 10 {
return
}
tmp := sum + index
set = append(set, index)
if tmp <= n {
dfs(index + 1, tmp) // 考虑当前元素,而且不可以重复考虑
}
set = set[: len(set) - 1]
dfs(index + 1, sum)
}
combinationSum4
func combinationSum4(nums []int, target int) int {
// dp := make(map[int]int)
// return helper(dp, nums, target)
dp := make([]int, target+1)
dp[0] = 1
for i:=1; i<=target; i++ {
for _, num := range nums {
if i >= num {
dp[i] += dp[i-num]
}
}
}
return dp[target]
}
func helper(dp map[int]int, nums []int, target int) int{
if v,ok := dp[target]; ok {
return v
}
cnt := 0
for i:=0; i<len(nums); i++ {
if nums[i] == target {
cnt++
} else if nums[i] < target{
cnt += helper(dp, nums, target - nums[i])
}
}
dp[target] = cnt
return cnt
}
permutations
也是同理啦这个,实际上就是这个套路,就是需要怎么变,我没想出来。。。
就是每次都是全部数用上,借用辅组数组来判断,选一个没用过的,每次遍历一下就好了
func permute(nums []int) [][]int {
res := make([][]int, 0)
if len(nums) == 0 {
return res
}
set := make([]int,0)
used := make([]bool, len(nums))
var dfs func()
dfs = func(){
if len(set) == len(nums) {
res = append(res, append([]int{}, set...))
}
for i:=0; i<len(nums); i++ {
if used[i] {
continue
}
used[i] = true
set = append(set, nums[i])
dfs()
set = set[:len(set)-1]
used[i] = false
}
}
dfs()
return res
}
permutations II
这里有个灵魂语句,需要判断是否重复呀,!used[i-1]
灵性!!!
假定出现重复的时候,如果前面的used[i-1]=false,那就是说明在这个循环里,已经用过了这个重复nums[i]了,因为用完会重新重置为false,这个时候就不需要再考虑这个重复变量append到set的位置了。used[i-1]=true,说明这个重复nums[i]是在之前的递归中被使用过的,位置不一样,可以append到set上。
func permuteUnique(nums []int) [][]int {
res := make([][]int, 0)
set := make([]int, 0)
used := make([]bool, len(nums))
var dfs func()
dfs = func() {
if len(set) == len(nums) {
res = append(res, append([]int{}, set...))
return
}
for i:=0; i<len(nums); i++ {
if used[i] {
continue
}
if i>0 && nums[i] == nums[i-1] && !used[i-1] { // importance!!!
continue
}
set = append(set, nums[i])
used[i] = true
dfs()
used[i] = false
set = set[:len(set) - 1]
}
}
dfs()
return res
}
93. Restore IP Addresses
func restoreIpAddresses(s string) []string {
res := make([]string,0)
set := make([]string,0)
length := len(s)
var dfs func(int)
dfs = func(cur int) {
if len(set) == 4 {
if cur == length {
str := set[0]
for i:=1; i<4; i++ {
str += "."+ set[i]
}
res = append(res, str)
}
return
}
if cur >= length {
return
}
var end int
if cur + 3 < length {
end = 3
} else {
end = length - cur
}
tmp := string(s[cur])
if tmp == "0"{
end = 1
}
for i:=1; i<=end; i++ {
tmp = string(s[cur:cur+i])
// fmt.Println(tmp)
if num,_ := strconv.Atoi(tmp);num > 255 {
break // note: Atoi return int error !
}
set = append(set, tmp)
dfs(cur+i)
set = set[:len(set)-1]
}
}
dfs(0)
return res
}
17. Letter Combinations of a Phone Number
func letterCombinations(digits string) []string {
str := strings.Split("abc def ghi jkl mno pqrs tuv wxyz"," ")
dic := make(map[int]string)
for i:=2;i<10;i++ {
dic[i] = str[i-2]
}
res := make([]string,0)
if len(digits)==0 {
return res
}
set := make([]string,0)
// fmt.Println(dic)
var dfs func(int)
dfs = func(cur int){
if cur == len(digits) {
tmp := ""
for _,s:=range set {
tmp += s
}
res = append(res, tmp)
return
}
ss := dic[int(digits[cur] - '0')]
// fmt.Println(ss)
for _,s := range ss {
set = append(set, string(s))
// fmt.Println(s)
dfs(cur+1)
set = set[:len(set)-1]
}
}
dfs(0)
return res
}
60. Permutation Sequence
// backtrace method --- my method
func getPermutation(n int, k int) string {
set := make([]int, 0)
used := make([]bool, n+1)
flag := false
var res string
var dfs func()
dfs = func() {
if flag {
return
}
if len(set) == n{
if k--; k==0 {
for _, num := range set {
res += strconv.Itoa(num)
}
flag = true
return
}
}
for i:=1;i<=n;i++ {
if used[i] {
continue
}
set = append(set,i)
used[i] = true
dfs()
if flag {
return
}
set = set[:len(set)-1]
used[i] = false
}
}
dfs()
return res
}
// math method -- dalao method
func getPermutation(n int, k int) string {
nAllPer := make([]int, n+1)
set := make([]int, n)
res := make([]int,0)
nAllPer[0]=1
for i:=1;i<=n;i++ {
nAllPer[i] = nAllPer[i-1]*i
}
for i:=0;i<n;i++{
set[i] = i+1
}
// fmt.Println(nAllPer)
k--
for i:=n-1;i>=0;i--{
index := k/nAllPer[i]
res = append(res,set[index])
set = append(set[:index], set[index+1:]...)
fmt.Println(set)
k -= index*nAllPer[i]
}
str := ""
for _, num := range res {
str += strconv.Itoa(num)
}
return str
}
31. Next Permutation
func nextPermutation(nums []int) {
length := len(nums)
cur := length-2
for ;cur>=0;cur--{
if nums[cur] < nums[cur+1] {
swp := cur
for i:=cur+1;i<length;i++ {
if nums[i] > nums[cur] {
swp = i
} else {
break
}
}
nums[swp], nums[cur] = nums[cur],nums[swp]
break
}
}
// fmt.Println(cur)
reverArr(&nums,cur+1, length-1)
}
func reverArr(arr *[]int, i, j int) {
for ;i<j;i,j= i+1,j-1 {
(*arr)[i],(*arr)[j] = (*arr)[j],(*arr)[i]
}
}
22. 括号生成
回溯加剪枝
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mXbUaY4G-1640531331612)(backtrace.assets/image-20210219105831264.png)]
- 当前左右括号都有大于
0
个可以使用的时候,才产生分支; - 产生左分支的时候,只看当前是否还有左括号可以使用;也就是
lRemain > 0
- 产生右分支的时候,还受到左分支的限制,右边剩余可以使用的括号数量一定得在严格大于左边剩余的数量的时候,才可以产生分支;也就是
rRemain > lRemain
- 在左边和右边剩余的括号数都等于
0
的时候结算。或者path长度等于2*n
func generateParenthesis(n int) []string {
res := make([]string, 0)
var helper func(lRemain, rRemain int, path string)
helper = func(lRemain, rRemain int, path string) {
if len(path) == 2 * n {
res = append(res, path)
}
if lRemain > 0 {
helper(lRemain - 1, rRemain, path + "(")
}
if lRemain < rRemain {
helper(lRemain, rRemain - 1, path + ")")
}
}
helper(n, n, "")
return res
}