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
中的任何数字。你可以按 任何 顺序返回答案。
示例 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
仅由数字组成
思路:
这个问题要求我们从一个只包含数字的字符串中插入 .
来生成所有可能的有效 IP 地址。我们可以用回溯法来解决这个问题
-
我们在字符串中插入
.
来划分 IP 地址的四个部分,寻找所有可能的划分方式。使用递归函数来探索每一个部分的可能性。 -
终止条件:
- 如果
dots == 3
且检查通过,则将拼接好的 IP 地址加入结果集。 - 如果
start
达到字符串的末尾,但没有生成有效的 IP 地址,则回溯。
- 如果
-
剪枝:
- 如果某个部分的长度大于3,或者某个部分的值超过255,立即回溯。
上代码:
class Solution {
public:
vector<string> restoreIpAddresses(string s) {
vector<string> res;
vector<string> current;
backtrack(s, 0, 0, current, res);
return res;
}
void backtrack(string& s, int start, int dots, vector<string>& current, vector<string>& res) {
// 如果已经插入了3个'.',则最后一个部分也必须是有效的IP段
if (dots == 3) {
if (isValid(s, start, s.size() - 1)) {
current.push_back(s.substr(start, s.size() - start));
res.push_back(join(current));
current.pop_back();
}
return;
}
// 尝试每个可能的IP段
for (int i = start; i < s.size(); ++i) {
if (isValid(s, start, i)) {
current.push_back(s.substr(start, i - start + 1));
backtrack(s, i + 1, dots + 1, current, res);
current.pop_back();
} else {
break; // 剪枝,提前退出
}
}
}
// 判断一个字符串的子串是否是有效的IP段
bool isValid(string& s, int start, int end) {
if (start > end) return false;
if (s[start] == '0' && start != end) return false; // 前导零的情况
int num = 0;
for (int i = start; i <= end; ++i) {
if (!isdigit(s[i])) return false;
num = num * 10 + (s[i] - '0');
if (num > 255) return false;
}
return true;
}
// 将当前IP段组合成一个完整的IP地址
string join(vector<string>& current) {
string ip;
for (int i = 0; i < current.size(); ++i) {
if (i != 0) ip += '.';
ip += current[i];
}
return ip;
}
};
78.子集
题目:
给你一个整数数组 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
中的所有元素 互不相同
思路:
要生成给定整数数组的所有可能子集(幂集),我们可以使用回溯算法来实现。
在每一步中,我们有两个选择:要么将当前元素加入到子集中,要么不加入。
每当处理完一个元素(即无论是否将其加入子集),我们将当前构建的子集加入到结果集中
当 达到数组末尾时,表示我们已经处理完了所有的元素,可以将当前的子集添加到解集中。
先递归探索不包括当前元素的子集,再探索包括当前元素的子集。
回溯时,撤销上一步的选择,继续探索下一种可能。
上代码:
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
vector<int> current;
backtrack(nums, 0, current, res);
return res;
}
void backtrack(vector<int>& nums, int index, vector<int>& current, vector<vector<int>>& res) {
// 将当前子集加入解集中
res.push_back(current);
// 从当前index开始,尝试每个元素
for (int i = index; i < nums.size(); ++i) {
// 做出选择:将nums[i]加入当前子集
current.push_back(nums[i]);
// 递归处理下一个元素
backtrack(nums, i + 1, current, res);
// 撤销选择,回溯
current.pop_back();
}
}
};
90.子集II
题目:
给你一个整数数组 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
思路:
当数组 nums
中可能包含重复元素时,生成所有可能的子集(幂集)并确保解集中不包含重复的子集,需要在回溯过程中进行处理。
为了避免生成重复的子集,我们需要在选择元素时进行额外的判断。
回溯思路:
-
排序:
- 首先,对数组
nums
进行排序。排序后,相同的元素会相邻排列,方便我们在递归过程中跳过重复元素。
- 首先,对数组
-
回溯函数设计:
index
表示当前要处理的元素在数组中的位置。- 每当处理完一个元素时,将当前构建的子集加入到结果集中。
- 为了避免重复子集的出现,当当前元素与前一个元素相同时,如果前一个元素未被选择,则跳过当前元素。
-
终止条件:
- 当
index
达到数组末尾时,表示我们已经处理完了所有的元素,可以将当前的子集添加到解集中。
- 当
上代码:
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<vector<int>> res;
vector<int> current;
sort(nums.begin(), nums.end()); // 先对数组进行排序
backtrack(nums, 0, current, res);
return res;
}
void backtrack(vector<int>& nums, int index, vector<int>& current, vector<vector<int>>& res) {
// 将当前子集加入解集中
res.push_back(current);
// 从当前index开始,尝试每个元素
for (int i = index; i < nums.size(); ++i) {
// 跳过重复元素
if (i > index && nums[i] == nums[i - 1]) continue;
// 做出选择:将nums[i]加入当前子集
current.push_back(nums[i]);
// 递归处理下一个元素
backtrack(nums, i + 1, current, res);
// 撤销选择,回溯
current.pop_back();
}
}
};