有效 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
中的任何数字。你可以按 任何 顺序返回答案。
示例 1:
输入:s = "25525511135" 输出:["255.255.11.135","255.255.111.35"]
示例 2:
输入:s = "0000" 输出:["0.0.0.0"]
示例 3:
输入:s = "101023" 输出:["1.0.10.23","1.0.102.3","10.1.0.23","10.10.2.3","101.0.2.3"]
提示:
1 <= s.length <= 20
s
仅由数字组成
思路:
这一题还是比较有难度的,属于一个回溯算法分割字符串的题目,核心点在于两个
1.怎么将字符串进行分割?
其实只要在原字符串的基础上进行操作就行了,有些人下意识会创建一个新的字符串来作为路径使用,其实并不需要,只要对字符串相应的地方进行insert一个'.’就可以了,回溯的时候再把这个.删掉,不过java的话修改不了String,新建一个StringBuffer就可以了。
2.怎么进行合法性检验
其实就是对于新分割出来的区域进行检验,只有新区域符合题目要求了,才能将进行下一步的递归操作,否则直接跳过,写出的检验函数合理即可
下面是代码
List<String> res;
StringBuffer path;
int count;//记录段数
public List<String> restoreIpAddresses(String s) {
res = new ArrayList<>();
path = new StringBuffer(s);
backTracking(s,0,0);
return res;
}
public void backTracking(String s ,int starIndex,int pointNum){
//pointNum用于记录当前字符串含有的.数量
//思路:每次截取一段字符串,用一个新函数检验是否合规,不合规直接continue掉
if(pointNum==3){
//收集结果
//前面已经有三段了,最后判断这一段合不合法就可以了
if(isLegal(path.toString(),starIndex,path.length())){
res.add(new String(path));
}
return;
}
//单层遍历
//因为下面选择用path进行判断,所以这里的长度得是path的长度,因为加了.之后长度会产生变化
for(int i = starIndex;i<path.length();i++){
//左闭右开
//控制好位数
if(i+1-starIndex>3)break;//超过3位就没有遍历的必要了
if(!isLegal(path.toString(),starIndex,i+1))continue;//不合法直接跳过
//切割字串
path.insert(i+1,'.');
pointNum++;
//对其它层进行递归
backTracking(s,i+2,pointNum);//因为加了个'.',所以在i+1的基础上再加1
//回溯
pointNum--;
//将添加的'.'删掉
path.delete(i+1,i+2);
}
}
public boolean isLegal(String s,int left,int right){
//注意这里的函数作用域为[left,right)
if(left>=right)return false;
if(s.charAt(left)=='0'&&right-left>1)return false;//有前导0的情况
String temp = s.substring(left,right);
int num = 0;
try {
num = Integer.parseInt(temp);
} catch (NumberFormatException e) {
//出现异常说明转换的数有非'0'-'9'的字符
//直接返回false
return false;
}
if(num>=0&&num<=255){
return true;
}
return false;
}
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3] 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0] 输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums
中的所有元素 互不相同
思路:
可以吧这些子集都当做是递归回溯算法上的节点,只要处于一个节点就添加节点的数据进去即可
代码如下:
List<List<Integer>> res;
List<Integer> path;
public List<List<Integer>> subsets(int[] nums) {
res = new ArrayList<>();
path = new ArrayList<>();
backTracking(nums,0);
return res;
}
public void backTracking(int[] nums,int starIndex){
res.add(new ArrayList<>(path));//将收集结果的过程放在最上面即可
//添加所有节点
if(starIndex>=nums.length)return;
for(int i =starIndex;i<nums.length;i++){
//添加节点
path.add(nums[i]);
backTracking(nums,i+1);
path.remove(path.size()-1);//回溯
}
}
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入:nums = [1,2,2] 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
示例 2:
输入:nums = [0] 输出:[[],[0]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
思路:
这一题咋一看与上一题很像,那能不能用上一题的解法呢?不能!
因为题目中是含有重复的数据的,而题目的条件是不能包含有重复的集合,所以我们要做的工作其实就是删除重复的集合而不删除数组中的重复元素,因为这些重复元素组成的集合也在我们的结果集内
要怎么去做呢?轮到used数组登场!
这个数组可以遍历到树层元素时,检验到上一位与现在遍历的元素重合的话就跳过,而一个路径中遍历到重复元素时却不会做任何操作,具体原理看我的注解吧
List<List<Integer>> res;
List<Integer> path;
boolean[] repeat;
public List<List<Integer>> subsetsWithDup(int[] nums) {
res = new ArrayList<>();
path = new ArrayList<>();
repeat = new boolean[nums.length];//使用这个办法要先排序
Arrays.sort(nums);
Arrays.fill(repeat,false);
backTracking(nums,0);
return res;
}
public void backTracking(int[] nums,int starIndex){
//通过repeat数组判断树层元素是否访问过
res.add(new ArrayList<>(path));
//添加所有节点
if(starIndex>=nums.length)return;
for(int i =starIndex;i<nums.length;i++){
//注意这里的数组一定是排序过的
//关键点在于!repeat[i-1],
if(i>=1&&nums[i]==nums[i-1]&&!repeat[i-1])continue;
//添加节点
path.add(nums[i]);
//这个true是为了让子树不受重复元素的影响
//因为子树的repeat[i-1]会一直为true,所以碰到了两个相同元素时上面的if不会执行
repeat[i] = true;
backTracking(nums,i+1);
//这次的回溯是为了让树层回到初始状态
//树层的repeat[i-1]一直为false,所以碰到了重复元素时必然直接跳过
repeat[i] = false;
path.remove(path.size()-1);//回溯
}
}
感谢观看!