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();
}
}
}