• 递归,回溯,剪枝


    对递归的理解很浅,经常困在底层递归中,有推荐看sicp前两章的
    记录下题目,斐波那契就跳过了

    递归汉诺塔

    题目链接

    • 递归
    func hanota(A []int, B []int, C []int) []int {
        n:=len(A)
        dfs(&A,&B,&C,n)
        return C
    }
    
    //递归函数,a通过b向c移动n个数据
    func dfs(a,b,c *[]int,n int){
        if n==1{
            *c = append(*c,(*a)[len(*a)-1])
            *a = (*a)[:len(*a)-1] //截断不包含后面的一个
        }
        if n>1{
            //n-1 通过c放b,1个底层放c,n-1通过a放c
            dfs(a,c,b,n-1)
            dfs(a,b,c,1)
            dfs(b,a,c,n-1)
        }
    }
    
    

    39 组合总和

    • dfs+回溯剪枝
    var res [][]int
    var path []int
    func combinationSum(candidates []int, target int) [][]int {
        res = make([][]int,0)
        path = make([]int,0)
        dfs(candidates,target,0,0)
        return res
    }
    
    func dfs(candidates []int,target int,index int,sum int){
        if sum>target{ 
            return
        }
        if sum==target{
            tmp:=make([]int,len(path)) //拷贝,直接放path在res,之后的path会改变,导致1最终结果错误
            copy(tmp,path)
            res = append(res,tmp)
            return
        }
    
        for i:=index;i<len(candidates);i++{
            sum+=candidates[i]
            path = append(path,candidates[i])
            dfs(candidates,target,i,sum)  //注意index为i
            path = path[:len(path)-1]
            sum-=candidates[i]
            
        }
    }
    

    40 组合总和2

    题目链接
    包含重复元素,可以使用重复,例如

    输入: candidates = [2,5,2,1,2], target = 5,
    输出:
    [
    [1,2,2],
    [5]
    ]
    

    需要1排序并设置used数组标识i位置是否已经使用,重复元素前面使用的情况下可以继续使用,如果前面false,则说明已经重复,不再使用
    说的有点绕,可以参考

    • 回溯+剪枝
    import "sort"
    var res [][]int
    var path []int
    
    func combinationSum2(candidates []int, target int) [][]int {
        //只使用一次,相同元素第一个使用的情况可以继续使用,否则不能重复使用,排序+flag
        used:=make([]bool,len(candidates))
        res=make([][]int,0)
        path = make([]int,0)
        sort.Ints(candidates)
        
        dfs(candidates,target,0,0,used)
        return res
    }
    
    func dfs(candidates []int,target int,index int,sum int,used []bool){
        if sum>target{
            return 
        }
        if sum==target{
            tmp:=make([]int,len(path))
            copy(tmp,path)
            res = append(res,tmp)
        }
    
        for i:=index;i<len(candidates);i++{
            if i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false{ //此种方式是在同层进行剪枝,前一个使用过则继续使用新的重复元素
                continue
            }
            sum+=candidates[i]
            path = append(path,candidates[i])
            used[i]=true
            dfs(candidates,target,i+1,sum,used)
            used[i] = false
            path = path[:len(path)-1]
            sum-=candidates[i]
        }
    }
    

    216 组和总和3

    和39类似,只是不同重复数字,并且范围到9

    • 回溯+剪枝
    var res[][]int
    var path []int
    func combinationSum3(k int, n int) [][]int {
        res = make([][]int,0)
        path = make([]int,0)
        dfs(1,0,k,n)
        return res
    }
    
    func dfs(index int,sum int,k int,target int){
        if sum>target{
            return 
        }
        if sum==target&&len(path)==k{
            res = append(res,append([]int{},path...))
            return 
        }
        for i:=index;i<10;i++{
            sum+=i
            path = append(path,i)
            dfs(i+1,sum,k,target)
            path = path[:len(path)-1]
            sum-=i
        }
    }
    

    77 组合

    题目链接

    • 类似上题
    var res [][]int
    var path []int
    func combine(n int, k int) [][]int {
        //全排列 需要index
        res = make([][]int,0)
        path = make([]int,0)
        dfs(1,n,k)
        return res
    }
    
    func dfs(index int,n int,k int){
        if len(path)==k{
            tmp:=make([]int,len(path))
            copy(tmp,path)
            res = append(res,tmp)
            return 
        }
    
        for i:=index;i<=n;i++{
            path = append(path,i)
            dfs(i+1,n,k)
            path = path[:len(path)-1]
        }
    }
    
    
    

    46 全排列

    • 不含重复数字
    var res [][]int
    var path []int
    
    func permute(nums []int) [][]int {
         //不需要index,used记录即可,因为排列1选完后面还可以选前面
        used:=make([]bool,len(nums))
        res = make([][]int,0)
        path = make([]int,0)
        dfs(nums,used)
        return res
    }
    
    func dfs(nums []int,used []bool){
        if len(path)==len(nums){
            tmp := make([]int,len(path))
            copy(tmp,path)
            res = append(res,tmp)
            return 
        }
        
        for i:=0;i<len(nums);i++{
            if used[i]{
                continue
            }
            
            path = append(path,nums[i])
            used[i] = true
            dfs(nums,used)
            used[i] = false
            path = path[:len(path)-1]
        }
    }
    

    47 全排列2

    题目链接

    • 排序+used标记,前面使用过不再使用.
      因为没有index,需要对此次used[i]进行判断,避免重复选取
    
    import "sort"
    var res [][]int
    var path []int
    
    func permuteUnique(nums []int) [][]int {
         //包含重复元素,需要排序并且对前面的剪枝
        sort.Ints(nums)
        used:=make([]bool,len(nums))
        res = make([][]int,0)
        path = make([]int,0)
        dfs(nums,used)
        return res
    }
    
    func dfs(nums []int,used []bool){
        if len(path)==len(nums){
            tmp := make([]int,len(path))
            copy(tmp,path)
            res = append(res,tmp)
            return 
        }
        
        for i:=0;i<len(nums);i++{
            if i>0&&nums[i]==nums[i-1]&&used[i-1]==false{ //前一个没用则不能用此次的,否则重复,[1,1,2],前面1false,则第二个1开头就不用再次计算
                continue
            }
            //此次元素已经使用过则不再使用
            if used[i]==true{
                continue
            }
            path = append(path,nums[i])
            used[i] = true
            dfs(nums,used)
            used[i] = false
            path = path[:len(path)-1]
        }
    }
    

    784 字母大小写全排列

    • index递归,大小写转换,ascii,a-32=A
    var  res []string
    func letterCasePermutation(s string) []string {
        //不算全排列,转换之后进行判断
        path:=[]byte(s)
        res = make([]string,0)
        dfs(path,0)
        return res
    }
    
    func dfs(path []byte,index int){
        //每次更改都放入res
        res =  append(res,string(path))
    
        for i:=index;i<len(path);i++{
            //数字不需要转换
            if isNumber(path[i]){
                continue
            }
            //字母转换,之后恢复
            if isChar(path[i]){
                path[i]-=32 //大小写相差32,小写大
            }else{
                path[i]+=32
            }
            dfs(path,i+1)
            if isChar(path[i]){
                path[i]-=32
            }else{
                path[i]+=32
            }
        }
    }
    
    //判断是否是数字
    func isNumber(str byte)bool{
        if str>='0'&&str<='9'{
            return true
        }else{
            return false
        }
    }
    
    //判断是否是字母,进行大小写转换
    func isChar(str byte)bool{
        if str>='a'&&str<='z'{
            return true
        }else{
            return false
        }
    }
    

    78 子集

    题目链接
    不包含重复元素的子集

    • index 递归回溯
    var res [][]int
    var path []int
    func subsets(nums []int) [][]int {
        //index递归,包含空集
        res = make([][]int,0)
        path = make([]int,0)
        dfs(nums,0)
        return res
    }
    
    func dfs(nums []int,index int){
        res = append(res,append([]int{},path...))
    
        for i:=index;i<len(nums);i++{
            path = append(path,nums[i])
            dfs(nums,i+1)
            path = path[:len(path)-1]
        }
    }
    

    90 子集II

    包含重复元素,不能重复子集,

    • index+排序+used数组去重
    import "sort"
    var res [][]int
    var path []int
    func subsetsWithDup(nums []int) [][]int {
        //index递归,包含重复元素,需要排序+used数组去除
        res = make([][]int,0)
        path = make([]int,0)
        used:=make([]bool,len(nums))
        sort.Ints(nums)
        dfs(nums,0,used)
        return res
    }
    
    func dfs(nums []int,index int,used []bool){
        res = append(res,append([]int{},path...))
    
        for i:=index;i<len(nums);i++{
            if i>0&&nums[i-1]==nums[i]&&used[i-1]==false{
                continue
            }
            used[i]=true
            path = append(path,nums[i])
            dfs(nums,i+1,used)
            path = path[:len(path)-1]
            used[i]=false
        }
    }
    

    139 单词拆分(*)

    判断是否单词是否能分解

    • dfs+记忆化
    func wordBreak(s string, wordDict []string) bool {
        //dfs + 记忆化   
        // index 每次+1
        used := make(map[int]bool,len(s))
        hash:=make(map[string]bool)
        for _,v:=range wordDict{
            hash[v]=true
        }
        return dfs(s,0,hash,used)
    }
    
    //dfs 细节很多 
    func dfs(s string,index int,hash map[string]bool,used map[int]bool)bool{
         if index==len(s){
             return true
         }
         if value,ok:=used[index];ok{ //有值则直接返回
             return value
         }
         for i:=index+1;i<=len(s);i++{   //==len(s) 不包含,
              str:=s[index:i] //取字符串
              if hash[str]&&dfs(s,i,hash,used){ //i会在下次循环+1
                  used[i] = true  //记录方便复用
                  return true
              }
         }
         used[index] = false//不存在也要标示
         return false
    }
    
    
    
    • dp
      dp[i] 代表0到i-1字符串是否符合,dp[0]=true
    func wordBreak(s string, wordDict []string) bool {
        //dp 
        //word放map 
        hash:=make(map[string]bool,0)
        for _,v:=range wordDict{
            hash[v]=true
        }
    
        dp:=make([]bool,len(s)+1)
        dp[0]=true
        for i:=1;i<=len(s);i++{   //还可以条件优化 dp[i]==true break.   dp[j]==false continue
            for j:=i-1;j>=0;j--{
                str:=s[j:i] //前段字符,从下表j到i-1
                if hash[str]&&dp[j]{ //dp[j] 为0到j-1,刚好覆盖
                    dp[i] = true
                }
            }
        }
        return dp[len(s)]
    }
    

    140 单词拆分2 (**)

    • hash+dfs
    func wordBreak(s string, wordDict []string) []string {
        // hash+dfs
        hash:=make(map[string]bool)
        for _,v:=range wordDict{
            hash[v]=true
        }
        var res []string
        var path []string
        var dfs func(index int)
        dfs = func(index int){
            if index==len(s){
                res = append(res,strings.Join(path," "))
                return 
            }
            for i:=index+1;i<=len(s);i++{
                if hash[s[index:i]]{ //符合才继续寻找,否则剪枝
                    path = append(path,s[index:i])
                    dfs(i)
                    path = path[:len(path)-1]
                }
            }
        }
        dfs(0)
        return res
    }
    

    131 分割回文串(*)

    dp和dfs结合的经典题目

    • dp+dfs
    var path []string
    var res [][]string
    func partition(s string) [][]string {
        //dp+fds
        path =make([]string,0)
        res = make([][]string,0)
    
        dp:=make([][]bool,len(s))
        for i:=0;i<len(s);i++{
            dp[i] = make([]bool,len(s))
        }
        
        //判断是否回文,并且存储
        for r:=0;r<len(s);r++{
            for l:=0;l<=r;l++{
                if s[l]==s[r]&&(r-l<=2||dp[l+1][r-1]){
                    dp[l][r] = true
                }
            }
        }
        dfs(s,0,dp)
        return res
    }
    
    func dfs(s string,index int,dp [][]bool){
        if index==len(s){
            tmp:=make([]string,len(path))
            copy(tmp,path)
            res = append(res,tmp)
            return 
        }
    
        for i:=index;i<len(s);i++{
            if dp[index][i]{ //回文则继续
                path = append(path,s[index:i+1]) //i不包含则应该+1才是正确范围
                dfs(s,i+1,dp)
                path = path[:len(path)-1]
            }
        }
    }
    
  • 相关阅读:
    js模板引擎v5
    js模板引擎v6
    python cookbook学习笔记[一次完成多个字符串的替换]
    您可能没注意到的ie6 select bug
    lambda参数写法问题
    python dict2种遍历方式及区别
    记一次 AXI id debug
    R2B fpga flow script
    axi 1.0
    advanced fpga design
  • 原文地址:https://www.cnblogs.com/9527s/p/15310150.html
Copyright © 2020-2023  润新知