代码随想录算法训练营Day30|回溯part03

112 阅读3分钟

LeetCode 93 复原IP地址

题目链接:leetcode.cn/problems/re…

文档讲解:programmercarl.com/0093.复原IP地址…

视频讲解:www.bilibili.com/video/BV1XP…

思路

考虑回溯三要素:

  1. 回溯函数的参数和返回值 参数:字符串s,下一子串的开始索引start 返回值:空 全局变量:结果集result,分割路径path
  2. 回溯函数的结束条件 当path长度为4时
    1. 如果start也等于s的长度,就把path中的子串用“.”连接,加入result
    2. 否则违背ip规则,直接返回
  3. 回溯搜索的遍历过程 对从start+1开始的三个分割位置循环(因为0~255最多是三位数)
    1. 分割出的子串是否满足,在0~255之间,没有前导0。不满足则跳过本次循环
    2. 把分割出的子串加入path
    3. 递归调用回溯函数
    4. 还原path

解法

class Solution {
	List<String> result;	
	List<String> path;
	
	public List<String> restoreIpAddresses(String s) {	
		result = new ArrayList<>();		
		path = new ArrayList<>();		
		backTracking(s, 0);		
		return result;		
	}
		  	
	public void backTracking(String s, int start) {	
		if (path.size() == 4) {		
			if (start == s.length()) {			
				StringBuilder ip = new StringBuilder();				
				for (int i = 0; i < path.size(); i++) {				
					ip.append(path.get(i) + ".");				
				}				
				ip.deleteCharAt(ip.length()-1);				
				result.add(ip.toString());				
				return ;			
			}			
			else {			
				return ;			
			}		
		}		
		for (int i = start+1; i <= Math.min(start+3, s.length()); i++) {		
			String ip = s.substring(start, i);			
			if (ip.length() > 1 && ip.charAt(0) == '0') {			
				continue;			
			}			
			if (Integer.valueOf(ip) > 255) {			
				continue;			
			}			
			path.add(ip);			
			backTracking(s, i);			
			path.remove(path.size()-1);		
		}		
	}
}

LeetCode 78 子集

题目链接:leetcode.cn/problems/su…

文档讲解:programmercarl.com/0078.子集.htm…

视频讲解:www.bilibili.com/video/BV1U8…

思路

由于子集的选取没有任何限制,在搜索这个树时所有节点都要向result中添加子集 考虑回溯三要素:

  1. 回溯函数的参数和返回值 参数:集合nums,开始索引start 返回值:空 全局变量:结果集result,选择路径path
  2. 回溯函数的结束条件 start等于nums长度,没有可选元素,返回
  3. 回溯搜索的遍历过程 从start开始的每个元素循环
    1. 加入path
    2. 向result中添加path副本
    3. 递归调用回溯函数
    4. 还原path

解法

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

LeetCode 90 子集II

题目链接:leetcode.cn/problems/su…

文档讲解:programmercarl.com/0090.子集II.h…

视频讲解:www.bilibili.com/video/BV1vm…

思路

nums中可能有重复元素,但子集和子集和之间不可重复,涉及到剪枝去重 考虑去重:

  1. 为了让相同的元素相邻,首先要对数组排序
  2. 和组合去重类似,如果一个元素与他前面的元素相同,仅当前面的元素也在子集中,才可以把此元素加入子集。故需要一个标志数组

考虑回溯三要素:

  1. 回溯函数的参数和返回值 参数:nums,开始索引start 返回值:空 全局变量:结果集result,选择路径path,标志数组inPath
  2. 回溯函数的结束条件 start等于nums长度,没有元素可选择,返回
  3. 回溯搜索的遍历过程 从start开始对每个元素i循环
    1. 如果i与前一个重复,且inPath[i-1]为假,跳过本次循环
    2. 把nums[i]加入path,inPath[i]置为真
    3. path加入result
    4. 递归调用回溯函数
    5. 还原path,inPath[i]置为假

解法

class Solution {
	List<List<Integer>> result;	
	List<Integer> path;	
	boolean[] inPath;
	
	public List<List<Integer>> subsetsWithDup(int[] nums) {	
		Arrays.sort(nums);		
		result = new ArrayList<>();		
		path = new ArrayList<>();		
		inPath = new boolean[nums.length];		
		result.add(new ArrayList<>(path));		
		backTracking(nums, 0);		
		return result;	
	}		  
	
	public void backTracking(int[] nums, int start) {	
		if (start == nums.length) {		
			return ;		
		}		
		for (int i = start; i < nums.length; i++) {		
			if (i > 0 && nums[i] == nums[i-1] && !inPath[i-1]) {			
				continue;			
			}			
			path.add(nums[i]);			
			inPath[i] = true;			
			result.add(new ArrayList<>(path));			
			backTracking(nums, i+1);			
			inPath[i] = false;			
			path.remove(path.size()-1);			
		}			
	}
}

今日收获总结

今日学习2h,去重之前都要对数组排序