代码随想录算法训练营第二十七天|93. 复原IP地址、78. 子集、90. 子集 II

LeetCode 93. 复原IP地址
题目链接:93. 复原 IP 地址 - 力扣(LeetCode)

​思路:

输入是字符串,返回的结果也要字符串,但其实该字符串由数字与'.'组成,因此可以考虑使用'.'.join()方法实现;当使用回溯时,终止判断条件应该为当遍历完最后一位时,且中间分为了4段合法有效的数字。每一层的判断就是看是否该段字符串的int在[0,255]区间内,同时如果长度大于1,要判断首位数字是否为0。

代码:

#python
class Solution:  
    def restoreIpAddresses(self, s: str) -> List[str]:  
        res, path = [], []  
        n = len(s)  
        if n == 0:  
            return res  
        def dfs(i):  
            if i == n and len(path) == 4:  
                res.append('.'.join(path.copy()))  
                return  
            for j in range(i, min(i + 3, n)):  # Fix here  //要注意j是从i开始,毕竟只有一位也可是可以的。
                t = s[i : j + 1]
                if 0 <= int(t) <= 255 and len(path) < 4:  //判断该范围组成的数是否满足条件
                    if len(t) > 1 and int(t[0]) == 0:  //判断首位是否为0
                        break
                    path.append(s[i : j + 1])  
                    dfs(j + 1)  
                    path.pop()  
        dfs(0)  
        return res
import java.util.ArrayList;
import java.util.List;

public class Solution {
    public List<String> restoreIpAddresses(String s) {
        List<String> res = new ArrayList<>();
        backtrack(s, res, new StringBuilder(), 0, 0);
        return res;
    }

    private void backtrack(String s, List<String> res, StringBuilder sb, int start, int count) {
        if (count == 4) {
            if (start == s.length()) {
                res.add(sb.toString());
            }
            return;
        }

        int len = sb.length();
        for (int i = 1; i <= 3 && start + i <= s.length(); i++) {
            String part = s.substring(start, start + i);
            if (isValid(part)) {
                if (count > 0) {
                    sb.append('.');
                }
                sb.append(part);
                backtrack(s, res, sb, start + i, count + 1);
                sb.setLength(len); // backtrack
            }
        }
    }

    private boolean isValid(String s) {
        if (s.length() > 1 && s.charAt(0) == '0') {
            return false; // Leading zero is not allowed
        }
        int num = Integer.parseInt(s);
        return num >= 0 && num <= 255;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        List<String> ips = solution.restoreIpAddresses("25525511135");
        for (String ip : ips) {
            System.out.println(ip);
        }
    }
}

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

​思路:

回溯算法中典型的对于每一位“选”与“不选”,若不选就直接调用进入下一层,如果选就将当前数加入path中,调用入下一层,并且要pop一下去除影响。题目已经规定了给的nums数字中每个数组互不相同,因此该题不用考虑去重部分。

代码:

#python
class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res, path = [], []
        n = len(nums)
        if n == 0:
            return res
        def backtracing(i):
            if i == n:
                res.append(path.copy())
                return 
            //不选,直接调用了
            backtracing(i + 1)
            // 当前要选,因此添加后调用,并且pop去除掉上一步操作
            path.append(nums[i])
            backtracing(i + 1)
            path.pop()
        backtracing(0)
        return res
/java
class Solution {
    List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
    LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
    public List<List<Integer>> subsets(int[] nums) {
        subsetsHelper(nums, 0);
        return result;
    }

    private void subsetsHelper(int[] nums, int startIndex){
        result.add(new ArrayList<>(path));//「遍历这个树的时候,把所有节点都记录下来,就是要求的子集集合」。
        // if (startIndex >= nums.length){ //终止条件可不加
        //     return;
        // }
        for (int i = startIndex; i < nums.length; i++){
            path.add(nums[i]);
            subsetsHelper(nums, i + 1);
            path.removeLast();
        }
    }
}


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

​思路:

该题相较于上题,多了一个nums数组中存在重复数字的条件,因此由于解集中不能包含重复的子集,我们需要去重。

给的第一种思路就是在最后判断条件时,使用Counter函数对于每一个res中的子集进行判断,看其是否字母分布频次相等于即将要加入的path数组,但是这样开销就会比较大了;

第二种思路,反正都是乱序的,那我们先对nums数组进行排序,这样相同的数就会排在一起了,当当前数与前一个数相同时,直接跳过,这样就以较小的开销代价规避掉了重复元素再进入path中引发的额外判断。

代码:

#python 思路一:Counter来检查重复
class Solution:
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        res, path = [], []
        n = len(nums)
        if n == 0:
            return res
        def backtracing(i):
            if i == n:
                for x in res:
                    if Counter(x) == Counter(path):   //开销巨大
                        return
                res.append(path.copy())
                return
            backtracing(i + 1)
            path.append(nums[i])
            backtracing(i + 1)
            path.pop()
        backtracing(0)
        return res
#python 思路二:排序后进行判断
class Solution:  
    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:  
        nums.sort()  # 对列表进行排序以避免重复  
        res = []  
          
        def backtrack(start, path):  
            res.append(path[:])  # 添加当前子集  
            for i in range(start, len(nums)):  
                if i > start and nums[i] == nums[i-1]:  # 跳过重复的元素  
                    continue  
                path.append(nums[i])  
                backtrack(i+1, path)  # 递归调用  
                path.pop()  # 回溯  
          
        backtrack(0, [])  
        return res
/java
class Solution {

  List<List<Integer>> res = new ArrayList<>();
  LinkedList<Integer> path = new LinkedList<>();
  
  public List<List<Integer>> subsetsWithDup( int[] nums ) {
    Arrays.sort( nums );
    traversal( nums, 0 );
    return res;
  }


  private void traversal( int[] nums, int start ) {
    res.add( new ArrayList<>( path ) );

    for ( int i = start; i < nums.length; i++ ) {
        // 跳过当前树层使用过的、相同的元素
      if ( i > start && nums[i - 1] == nums[i] ) {
        continue;
      }
      path.add( nums[i] );
      traversal( nums, i + 1 );
      path.removeLast();
    }
  }

}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

别看了真C不了一点

打赏者皆为义父

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

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

打赏作者

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

抵扣说明:

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

余额充值