算法学习——回溯场景

回溯

存在模板:

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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值