leetcode之回溯刷题总结3

leetcode之回溯刷题总结3

1-复原IP地址
题目链接:题目链接戳这里!!!

思路:回溯法
依次枚举截取的字符串,如果满足要求,继续枚举,否则,回到原来的状态,直到每次出现了三个逗号,并且后面的字符串满足IP地址的要求,则最终的合格IP地址加入集合。

class Solution {
      List<String> res = new ArrayList<>() ;
      StringBuffer path = new StringBuffer() ;
    public List<String> restoreIpAddresses(String s) {
      if(s.length()<4 || s.length()>12){
          return new ArrayList<>() ;
      }
      dfs(s,0,0) ;
      return res ;
    }
    public void dfs(String s, int start, int cnt){
        if(cnt == 3 && isValid(s.substring(start,s.length()))){
            res.add(path.append(s.substring(start,s.length())).toString()) ;
            return ;
        }
        for(int i=start; i<s.length(); i++){
        if(isValid(s.substring(start,i))){
            path.append(s.substring(start,i)) ;
            path.append('.') ;
            cnt ++ ;
            dfs(s,i,cnt) ;
            cnt -- ;
            path.delete(start+cnt,path.length()) ;
        }
        }
    }
    public boolean isValid(String s){
        if(s.length()==0){
            return false ;
        }
        if(s.charAt(0)=='0' && s.length()>1){
            return false ;
        }
        int ans = 0 ;
        for(int i=0; i<s.length(); i++){
            ans = ans * 10 + (s.charAt(i) - '0') ;
        }
        if(ans>=0 && ans<=255){
            return true ;
        }
        return false ;
    }
}

在这里插入图片描述
2-目标和
题目链接:题目链接戳这里!!!

思路:回溯法
每次尝试加或者减,回溯过程维护一个计数器cnt,如果枚举到最后一个且表达式的值等于target,则cnt++ 。

class Solution {
    int cnt = 0 ;
    public int findTargetSumWays(int[] nums, int target) {
        dfs(nums, target, 0, 0) ;
        return cnt ;
    }
    public void dfs(int [] nums, int target, int index, int sum){
        if(index==nums.length){
            if(sum == target){
                cnt ++ ;
            }
        }else{
            dfs(nums, target, index+1, sum+nums[index]) ;
            dfs(nums, target, index+1, sum-nums[index]) ;
        }

    }
}


思路2:动态规划

记数组的元素和为sum,添加- 号的元素之和为 neg,则其余添加+ 的元素之和为sum−neg,得到的表达式的结果为
neg=(sum-target)/2,这样就变成动态规划问题,从nums中的前i个中选取元素,使其和等于neg的方案数。

状态转移方程如下:
在这里插入图片描述

class Solution {
    int cnt = 0 ;
    public int findTargetSumWays(int[] nums, int target) {
      //动态规划
      int sum = 0 ;
      for(int i=0; i<nums.length; i++){
          sum += nums[i] ;
      }
      int diff = sum - target ;
      if(diff<0 || diff%2==1){
          return 0 ;
      }
      int neg = diff / 2 ;
      int [][] dp = new int [nums.length+1][neg+1] ;
      dp[0][0] = 1 ;
      for(int i=1; i<nums.length+1; i++){
          int num = nums[i-1] ;
          for(int j=0; j<neg+1; j++){
              dp[i][j] = dp[i-1][j] ;
              if(j>=num){
                  dp[i][j] += dp[i-1][j-num] ;
              }
          }
      }
      return dp[nums.length][neg] ;
    }
}

在这里插入图片描述
3-划分为k个相等的子集
题目链接:题目链接戳这里!!!

思路:有k个桶,每次尝试着向桶里加元素,从大到小加,用过的就不要再使用,超过目标值不使用当前元素,如果当前元素和下一个元素相等,当前元素不选,则下一个元素也不选,如果前k-1个满足条件,则第k个一定也满足条件。

class Solution {
    public boolean canPartitionKSubsets(int[] nums, int k) {
        int sum=0;
        boolean[] used=new boolean[nums.length];
        Arrays.sort(nums);
        for(int i=0;i<nums.length;i++)
        {
            sum+=nums[i];
        }
        if(sum%k!=0)
            return false;
        int target=sum/k;
        if(nums[nums.length-1]>target)
            return false;
        return dfs(nums,nums.length-1,target,0,k,used);
    }

    public static boolean dfs(int[] nums,int begin,int target,int curSum,int k,boolean[] used)
    {
        //k==1就说明一定满足要求,不用k==0
        if(k==1)
            return true;
        if(curSum==target)
            return dfs(nums,nums.length-1,target,0,k-1,used);//找到了一个组合,还有k-1个.
        //从大到小,遍历次数少一些
        for(int i=begin;i>=0;i--)
        {
            //使用过的元素就不能再使用了
            if(used[i])
                continue;
            //超过目标值,不能选当前元素
            if(curSum+nums[i]>target)
                continue;
            used[i]=true;
            if(dfs(nums,i-1,target,curSum+nums[i],k,used))
                return true;
            used[i]=false;
            while(i>0&&nums[i-1]==nums[i])//如果当前元素不选,则,下一个一样的元素也不选
                i--;
        }
        return false;
    }
}


在这里插入图片描述
4-累加数
题目链接:题目链接戳这里!!!

思路:回溯法
这题有点坑,就是需要用Long型,否则会报错,因为num的最长长度到达了35.

用一个集合res存储拆分的整数,如果拆分到最后且集合的大小大于等于3,说明满足条件。

class Solution {
    public boolean isAdditiveNumber(String num) {
        List<Long> res = new ArrayList<>() ;
        return dfs(res,num,0) ;
    }
    public boolean dfs(List<Long> res, String num, int start){
        if(start==num.length() && res.size()>=3){ //拆分完成,集合中元素大于等于3,满足要求
            return true ;
        }
        for(int i=start; i<num.length(); i++){
            if(num.charAt(start)=='0' && i>start){ //第一位是0,且大于等于两位数,不合法
                break ;
            }
            long n = sub(num, start, i) ;
        
            int size = res.size() ;
            if(size>=2 && n > res.get(size-1) + res.get(size-2)){ //当前截取的不满足要求,后面的更大,自然也不满足要求
                break ;
            }
            if(size<=1 || n == res.get(size-1) + res.get(size-2)){
                res.add(n) ;
                if(dfs(res,num,i+1)){
                    return true ;
                }
                res.remove(res.size()-1) ;
            }
        }
        return false ;
    }
    public long sub(String s, int x, int y){
        long ans = 0 ;
        for(int i=x; i<=y; i++){
            ans = ans * 10  + (s.charAt(i) - '0') ;
        }
        return ans ;
    }
}

在这里插入图片描述
5-火柴拼正方形
题目链接:题目链接戳这里!!!

思路:拼正方形,就是将数组元素划分成四个和相等的子集,使用回溯法,不挺的尝试,如果每个子集满足要求,则继续搜索其它子集,直到3个 子集满足要求,则最后一个子集也一定满足要求,故可以拼成正方形。

class Solution {
    public boolean makesquare(int[] matchsticks) {
        //将数组划分成4个和相等的子集
        if(matchsticks.length<4){//不满足要求
            return false ;
        }

        int sum = 0 ;
        for(int num : matchsticks){
            sum += num ;
        }
        if(sum % 4 != 0){ //不满足要求
            return false ;
        }
        boolean [] vis = new boolean [matchsticks.length] ;
        return dfs(matchsticks,4,sum/4,0,0,vis) ;
    }
    public boolean dfs(int [] matchsticks, int k, int target, int start, int curSum, boolean [] vis){
        if(k==1){ //前3个都满足要求,第四个一定满足
            return true ;
        }
        if(curSum==target){//出现一条边满足要求
            return dfs(matchsticks,k-1,target,0,0,vis) ;
        }
        for(int i=start; i<matchsticks.length; i++){
            if(vis[i]){ //访问过不再访问
                continue ;
            }
            vis[i] = true ;
            if(dfs(matchsticks,k,target,i+1, curSum+matchsticks[i],vis)){
                return true ;
            }
            vis[i] = false ;
        }
        return false ;
    }
    
}

在这里插入图片描述
6-模糊坐标
题目链接:题目 链接戳这里!!!

思路:枚举法
我们首先把这个二维坐标分成两部分,前一部分表示 x 坐标,后一部分表示 y 坐标。例如当给出的二维坐标为 (1234) 时,我们可以把它分成 1, 234,12, 34 和 123, 4 三种情况。随后对于每一部分,我们再考虑是否可以添加小数点以及在哪里添加小数点。例如,对于 123,合法的坐标有 1.23,12.3 和 123。

在处理每一部分时,我们需要将出现多余 0 的不合法的坐标去除。如果我们不添加小数点,那么这个坐标不能有前导 0;如果我们在某个位置添加小数点,那么整数部分不能有前导 0,小数部分的末尾也不能有 0。

class Solution {
    public List<String> ambiguousCoordinates(String s) {
        List<String> ans = new ArrayList<>() ;
        for(int i=2; i<s.length()-1; i++){
            for(String left : f(s,1,i)){
                for(String right : f(s,i,s.length()-1)){
                    ans.add("(" + left + ", " + right + ")") ; 
                }
            }
        }
        return ans ;
    }
    public List<String> f(String s, int i, int j){
        List<String> ans = new ArrayList<>() ;
        for(int d=1; d<=j-i; d++){
            String left = s.substring(i, i+d) ;
            String right = s.substring(i+d, j) ;
            if((!left.startsWith("0") || left.equals("0")) && !right.endsWith("0"))
            ans.add(left + ((d<j-i) ? "." : "") + right) ;
        }
        return ans ;
    }
}

在这里插入图片描述
**

无论你遇到什么挫折,要以追求自我的心态去理解世界的规律,并因此明白所有努力都有意义,享受努力带给自己的改变!!!

**

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nuist__NJUPT

给个鼓励吧,谢谢你

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值