93.复原IP地址
有效 IP 地址 正好由四个整数(每个整数位于 0
到 255
之间组成,且不能含有前导 0
),整数之间用 '.'
分隔。
- 例如:
"0.1.2.201"
和"192.168.1.1"
是 有效 IP 地址,但是"0.011.255.245"
、"192.168.1.312"
和"192.168@1.1"
是 无效 IP 地址。
给定一个只包含数字的字符串 s
,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s
中插入 '.'
来形成。你 不能 重新排序或删除 s
中的任何数字。你可以按 任何 顺序返回答案。
思路
该问题其实就是切割问题,与 131.分割回文子串 类似,沿用相同的思路即可解题。
需要注意的是终止条件与下次遍历传入的坐标位置
终止条件不能以分割线结束作为判断,而应根据填入的逗点数量判断,当逗点数量为3时说明已经将s分割为4段了。然后再判断最后一段子串是否符合IP地址。
下次遍历的开始坐标应该为i+2,这是因为前面插入了逗点字符。
代码实现
class Solution {
List<String> res = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
if (s.length() > 12){
return res;
}
backtracking(s,0,0);
return res;
}
private void backtracking(String s,int startIdx,int pointNum){
// 终止条件:当字符串被分割为4段,也就是当pointNum为3时
if(pointNum==3){
// 判断第4段子串是否合法
if(isValid(s,startIdx,s.length()-1)){
res.add(s);
}
return;
}
// 遍历
for(int i=startIdx;i<s.length();i++){
if(isValid(s,startIdx,i)){
// 在str的后面加上 .
s = s.substring(0, i + 1) + "." + s.substring(i + 1);
pointNum++;
// 插入.后下一个位置为i+2
backtracking(s,i+2,pointNum);
// 回溯
pointNum--;
s= s.substring(0,i+1) + s.substring(i+2);
}else {
break;
}
}
}
/**
* 判断字符串[start,end]是否为有效的IP地址段
*/
private boolean isValid(String s,int start,int end){
if (start > end) {
return false;
}
if (s.charAt(start) == '0' && start != end) { // 0开头的数字不合法
return false;
}
int num = 0;
for (int i = start; i <= end; i++) {
if (s.charAt(i) > '9' || s.charAt(i) < '0') { // 遇到⾮数字字符不合法
return false;
}
num = num * 10 + (s.charAt(i) - '0');
if (num > 255) { // 如果⼤于255了不合法
return false;
}
}
return true;
}
}
78.子集
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
思路
该问题其实与组合问题类似,只是需要特别留意与组合不同的地方:组合问题的解是叶子节点,而本题的解是所有的节点。
代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtracking(nums,0);
return res;
}
private void backtracking(int[] nums,int startIdx){
// 遍历时将所有节点记录下来
res.add(new ArrayList(path));
// 终止条件:当剩余的元素为空时(可以不加,因为遍历循环结束)
if(startIdx>=nums.length){
return;
}
for(int i=startIdx;i<nums.length;i++){
path.add(nums[i]);
backtracking(nums,i+1);
// 回溯
path.remove(path.size()-1);
}
}
}
90子集II
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
思路
与78.子集问题不同的是,本题的数组中包含相同的元素,所以需要去重处理。
去重操作的思路:
1.对数组排序
2.判断同一数层相邻元素是否相同,如果相同则跳过本层
代码
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> subsetsWithDup( int[] nums ) {
Arrays.sort( nums );
subsetsWithDupHelper( nums, 0 );
return res;
}
private void subsetsWithDupHelper( 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] );
subsetsWithDupHelper( nums, i + 1 );
path.removeLast();
}
}
}