前言

今天是学习回溯算法的第三天,我们继续来一起学习回溯算法蕴含的逻辑处理,希望博主记录的内容能够对大家有所帮助 ,一起加油吧朋友们!💪💪💪

复原IP地址

LeetCode题目链接

这道题就是给一个字符串,让我们通过插入三个.来使得其成为一个ip地址,然后判断ip地址的有效性,组成ip地址的四个整数需要为0~255之间

1728870242781

我们来思路梳理

我们来进一步梳理回溯三要数

1
2
3
4
5
6
//s: 输入的字符串
//startIndex: 当前划分 IP 段的起始位置
//path: 当前已经划分出的 IP 段
//不需要返回值因为结果被直接存到结果集result中
List<String> result = new ArrayList<>();
private void backtracking(String s, int startIndex, List<String> path){}
1
2
3
4
5
6
7
8
// 终止条件:path 已经有 4 段 IP 且正好用完字符串
if (path.size() == 4) {
if (startIndex == s.length()) {
// 形成合法 IP,加入结果集
result.add(String.join(".", path));
}
return; // 否则返回,不再继续处理
}
1
2
3
4
5
6
7
8
9
10
11
//单层搜索过程,从 startIndex 开始,尝试截取 1 到 3 个字符组成 IP 地址段
for(int i = startIndex; i < s.length() && i < startIndex + 3; i++){
String segment = s.substring(startIndex, i + 1);

//判断当前子串是否合法的ip段
if(isValidSegment(segment)){
path.add(segment);
backtracking(s, i + 1, path);
path.removeLast();//回溯
}
}

回溯的完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Solution {
List<String> result = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
//如果字符串长度过长或过短,不可能形成合法 IP,直接返回空结果
if(s.length() < 4 || s.length() > 12){
return result;
}
backtracking(s, 0, new ArrayList<>());
return result;
}

private void backtracking(String s, int startIndex, List<String> path){
// 终止条件:path 已经有 4 段 IP 且正好用完字符串
if(path.size() == 4){
if(startIndex == s.length()){
result.add(String.join(".", path));
}
return;
}


//单层搜索过程,从 startIndex 开始,尝试截取 1 到 3 个字符组成 IP 地址段
for(int i = startIndex; i < s.length() && i < startIndex + 3; i++){
String segment = s.substring(startIndex, i + 1);

//判断当前子串是否合法的ip段
if(isValidSegment(segment)){
path.add(segment);
backtracking(s, i + 1, path);
path.removeLast();//回溯
}
}
}

// 判断一个字符串是否是合法的 IP 地址段
private boolean isValidSegment(String segment){
// 如果以 '0' 开头且长度大于 1,则是无效的,因为不能有前导 0
if(segment.length() > 1 && segment.charAt(0) == '0'){
return false;
}
//将字符串转为整数,检查是否在合法范围内
int num = Integer.parseInt(segment);
return num >= 0 && num <= 255;
}
}

子集

LeetCode题目链接

这道题就是给一个整数数组返回它所有的子集

1728877072322

我们来思路梳理

我们来进一步梳理回溯三要素

1
2
3
4
5
6
//nums: 输入的数组
//startIndex: 当前递归到数组的哪个位置
//path: 当前构建的子集
//result: 存储所有可能的子集的列表
List<List<Integer>> result = new ArrayList<>();
private void backtracking(int[] nums, int startIndex, List<Integer> path) {}
1
2
//在这个问题中,不需要显式的终止条件,因为我们每次都将当前路径(子集)加入结果集,递归自然会结束,直到遍历完整个数组🤔🤔🤔
result.add(new ArrayList<>(path));//每次递归都将当前路径(子集)加入结果集
1
2
3
4
5
for (int i = startIndex; i < nums.length; i++) {
path.add(nums[i]);// 选择当前数字加入子集
backtracking(nums, i + 1, path);// 递归,继续选择下一个数字(从 i+1 开始)
path.remove(path.size() - 1); // 回溯,移除当前数字,尝试其他组合
}

完整的回溯代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtracking(nums, 0, new ArrayList<>());
return result;
}

private void backtracking(int[] nums, int startIndex, List<Integer> path){
result.add(new ArrayList<>(path));

for(int i = startIndex; i < nums.length; i++){
path.add(nums[i]);
backtracking(nums, i + 1, path);
path.removeLast();
}
}
}

子集II

LeetCode题目链接

这道题相较于上题而言,改变的是所给的整数数组的数可能有重复

1728879111323

我们来思路梳理

我们来进行回溯三要素梳理

1
2
3
4
5
6
7
//nums: 输入的整数数组(可能包含重复元素🤔)
//startIndex: 当前递归处理到的数组起始位置
//path: 当前构造中的子集
//result: 存储所有可能的子集的列表
//递归函数不需要返回值,结果将直接存入 result 列表中
List<List<Integer>> result = new ArrayList<>();
private void backtracking(int[] nums, int startIndex, List<Integer> path) {}
1
result.add(new ArrayList<>(path));//每次递归都将当前路径(子集)加入结果集
1
2
3
4
5
6
7
8
//单层搜索过程:从 startIndex 开始,遍历数组
for (int i = startIndex; i < nums.length; i++) {
//去重
if (i > startIndex && nums[i] == nums[i - 1]) continue; // 跳过重复的元素
path.add(nums[i]);
backtracking(nums, i + 1, path); //注意 i + 1 是因为不能重复选择当前元素
path.remove(path.size() - 1);//回溯
}

回溯的完整代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);//首先对数组进行排序,这样相同的元素会相邻,方便后续去重处理
backtracking(nums, 0, new ArrayList<>());
return result;
}
private void backtracking(int[] nums, int startIndex, List<Integer> path){
result.add(new ArrayList<>(path));

for(int i = startIndex; i < nums.length; i++){
if(i > startIndex && nums[i] == nums[i - 1]) continue;
path.add(nums[i]);
backtracking(nums, i + 1, path);
path.removeLast();
}
}
}

总结

今天的回溯就学到这里啦,这几道题的处理都是和前几天的处理逻辑大差不差,去重是一样的排序逻辑,后续博主也将会开个项目专栏分享项目学习的经验,大家一起加油,奥利给✊✊✊