代码随想录Day23 回溯算法 LeetCode T93 复原ip地址

 LeetCode T93 复原ip地址

题目链接 :

93. 复原 IP 地址 - 力扣(LeetCode)

题目思路:

首先我们可以进行一次剪枝,首先正确的ip地址要在12位,所以如果字符串的长度大于12我们就直接进行剪枝,接下来进行回溯函数的逻辑书写,我们知道正确的ip地址之间是用.分割的,当.的数量达到3个的时候也就是我们开始收集结果的时候了,别忘了收集结果的时候也要添加最后一段字符串,显而易见我们这里回溯的参数就有了,一个pointNum来记录我们的'.'字符个数,我们可以选择更快避免拷贝的StringBuilder ,这里我们就直接对字符串s进行修改

1.回溯函数参数

public void backtracking(String s,int startIndex,int pointNum)

2.终止条件

        if(pointNum == 3)
        {
           if(isValid(s,startIndex,s.length()-1))
            {
                result.add(s);
                return;
            }
        }

3.for循环

        for(int i = startIndex;i<s.length();i++)
        {
            if(isValid(s,startIndex,i))
            {
                s = s.substring(0,i+1)+"."+s.substring(i+1);
                pointNum++;
                backtracking(s,i+2,pointNum);
                pointNum--;
                s = s.substring(0,i+1) + s.substring(i+2);
            }
            else
            {
                break;
            }

        }

4.isVaild函数

这里笔者犯了一个小错误,我误将'0'字符的ascll码强转为了int类型,导致将字符转整形失败了

char a = '0';
int b = (int)a;  //48
int c = a - '0';//0
 public boolean isValid(String s,int begin,int end)
        {
            if(begin>end)
            {
                return false;//没有元素
            }
            if(s.charAt(begin) == '0' && begin != end)
            {
                return false;//首位为0并且不是唯一一位,不符合
            }
            int num = 0;
            for(int i = begin;i<=end;i++)
            {
                if(s.charAt(i)>'9' || s.charAt(i)<'0')
                {
                    return false;//每个字符不再'0-9'之间
                }
                num = num*10+(int)s.charAt(i)-48;
                if(num>255)
                {
                    return false;//不符合IP地址
                }
               
            }
             return true;

        }

题目代码:

lass Solution {
    List<String> result = new ArrayList<>();
    public List<String> restoreIpAddresses(String s) {
        if(s.length()>12)
        {
            return result;
        }
        backtracking(s,0,0);
        return result;

    }
    public void backtracking(String s,int startIndex,int pointNum)
    {
        //终止条件
        if(pointNum == 3)
        {
           if(isValid(s,startIndex,s.length()-1))
            {
                result.add(s);
                return;
            }
        }
        //for循环
        for(int i = startIndex;i<s.length();i++)
        {
            if(isValid(s,startIndex,i))
            {
                s = s.substring(0,i+1)+"."+s.substring(i+1);
                pointNum++;
                backtracking(s,i+2,pointNum);
                pointNum--;
                s = s.substring(0,i+1) + s.substring(i+2);
            }
            else
            {
                break;
            }

        }
  
    }
          public boolean isValid(String s,int begin,int end)
        {
            if(begin>end)
            {
                return false;
            }
            if(s.charAt(begin) == '0' && begin != end)
            {
                return false;
            }
            int num = 0;
            for(int i = begin;i<=end;i++)
            {
                if(s.charAt(i)>'9' || s.charAt(i)<'0')
                {
                    return false;
                }
                num = num*10+(int)s.charAt(i)-48;
                if(num>255)
                {
                    return false;
                }
               
            }
             return true;

        }
}

LeetCode T78 子集

题目链接:78. 子集 - 力扣(LeetCode)

题目思路

这题的思路很简单,直接照搬回溯的代码模板即可,我们将模板的思路再过一遍

这里我们只需要创建一个result来保存结果,创建一个path来保存每一条正确的路径即可

这题我们要记录树的所有节点

1.回溯函数的参数

这里我们只需要一个nums数组和startIndex即可,确定每次开始的位置就可以

 public void backtracking(int[] nums,int startIndex)

2.终止条件

这里没有什么终止条件,只要startIndex不超过nums.length即可,当然有for循环帮我们限制,所以可以直接省略这个终止条件

3.一次搜索逻辑(for循环)

我们只需要加入一次path,递归再回溯即可

        for(int i = startIndex;i<nums.length;i++)
        {
            path.add(nums[i]);
            backtracking(nums,i+1);
            path.remove(path.size()-1);

        }

题目代码

class Solution {
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        backtracking(nums,0);
        return result;
    }
    public void backtracking(int[] nums,int startIndex)
    {
        result.add(new ArrayList(path));
        for(int i = startIndex;i<nums.length;i++)
        {
            path.add(nums[i]);
            backtracking(nums,i+1);
            path.remove(path.size()-1);

        }
    }
}

LeetCode T90 子集II

题目链接:90. 子集 II - 力扣(LeetCode)

题目思路

这题的思路和我们之前做过的组合问题II一样,我们都需要标记用过的元素来进行去重的操作,也就是我们之前说过的树层去重的操作

去重逻辑:

从上图我们知道,我们重复元素[1,1,2]中是不允许两个[1,2]同时存在的,所以在前两个元素相同时,我们可以舍弃第二个元素开始的递归,也就是舍弃第二条path,但是前提我们要先对这个数组进行排序,这样才能让两个相同元素靠在一起,我们才方便对两个元素中的后一个的递归进行舍弃,代码如下:

 if(i>0 && nums[i] == nums[i-1]&& !used[i-1])

1.递归参数

只需要nums数组和startIndex即可,由于其他变量都定义为全局变量了

public void backtracking(int[] nums,int startIndex)

2.终止条件

由于这道题我们要计算空集在内,所以我们要先收集结果,这题的所有节点都是我们需要的结果

当startIndex超过我们nums的长度,那么我们就停止递归


        if(startIndex>=nums.length)
        {
            return;
        }

3.一次搜索逻辑

我们首先要判断该path是否有效,有就path记录,记录used数组,递归.path回溯

        for(int i = startIndex;i<nums.length;i++)
        {
            if(i>0 && nums[i] == nums[i-1]&& !used[i-1])
            {
                continue;
            }
            path.add(nums[i]);
            used[i] = true;
            backtracking(nums,i+1);
            path.remove(path.size()-1);
            used[i] = false;
        }

题目代码

class Solution {
    boolean[] used;
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        if(nums.length == 0)
        {
            result.add(path);
            return result;
        }
        used  = new boolean[nums.length];
        Arrays.sort(nums);
        backtracking(nums,0);
        return result;

    }
    public void backtracking(int[] nums,int startIndex)
    {
        result.add(new ArrayList(path));
        if(startIndex>=nums.length)
        {
            return;
        }
        for(int i = startIndex;i<nums.length;i++)
        {
            if(i>0 && nums[i] == nums[i-1]&& !used[i-1])
            {
                continue;
            }
            path.add(nums[i]);
            used[i] = true;
            backtracking(nums,i+1);
            path.remove(path.size()-1);
            used[i] = false;
        }

    }
}