_24LeetCode代码随想录算法训练营第二十四天-回溯算法 | 93.复原IP地址、78.子集、90.子集II
题目列表
- 93.复原IP地址
- 78.子集
- 90.子集II
93.复原IP地址
代码随想录地址:
https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html
题目
有效 IP 地址 正好由四个整数(每个整数位于 0
到 255
之间组成,且不能含有前导 0
),整数之间用 '.'
分隔。
- 例如:
"0.1.2.201"
和"192.168.1.1"
是 有效 IP 地址,但是 “0.011.255.245”(因为前导有0)、“192.168.1.312” (312大于255)和 “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
仅由数字组成
思路
代码
/*
* @lc app=leetcode.cn id=93 lang=cpp
*
* [93] 复原 IP 地址
*/
// @lc code=start
class Solution {
private:
vector<string> res;//存储结果
//判断字符串是否合法(该字符串就是计划为一个数字)
//是前闭后闭区间
bool isValid(string& s, int start, int end)
{
//如果开始的区间大于结束的区间,则返回false
if(start > end)
return false;
//如果前导数字为0,则非法
if(s[start] == '0' && start != end)
return false;
//判断数字的大小是否小于等于255
int num = 0;
for(int i = start; i <= end; i++)
{
if(s[i] < '0' && s[i] > '9')
return false;
num = num * 10 + s[i] - '0';
if(num > 255)
return false;
}
return true;
}
//回溯算法
void backtracking(string& s, int start, int pointNum)
{
//终止条件
if(pointNum == 3)
{
//检查第四个数是否满足要求
//如果满足要求,就将s加入res中
if(isValid(s, start, s.size()-1))
res.push_back(s);
return;
}
//单层循环
for(int i = start; i < s.size(); i++)
{
//检查字符串是否满足要求,如果满足要求就加点
if(isValid(s, start, i))
{
s.insert(s.begin() + i + 1, '.');
pointNum++;
//递归
//因为加了点,因此需要将i+2作为这层递归的起始位置
backtracking(s, i + 2, pointNum);
//回溯
pointNum--;
s.erase(s.begin() + i + 1);
}
}
}
public:
vector<string> restoreIpAddresses(string s) {
res.clear();
backtracking(s, 0, 0);
return res;
}
};
// @lc code=end
78.子集
代码随想录地址:
https://programmercarl.com/0078.%E5%AD%90%E9%9B%86.html
题目
给你一个整数数组 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
中的所有元素 互不相同
思路
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!
题目中nums中的每个元素不重复,要求解集不包含重复的子集,所以不能往前遍历,要设置参数start。
代码
/*
* @lc app=leetcode.cn id=78 lang=cpp
*
* [78] 子集
*/
// @lc code=start
class Solution {
private:
vector<vector<int>> res;
vector<int> path;
void backtracking(vector<int>& nums, int start)
{
res.push_back(path);//就是所有的结果都要,因此每次递归都收集结果
//终止条件
if(start >= nums.size())
return;
//单层for循环
for(int i = start; i < nums.size(); i++)
{
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
res.clear();
path.clear();
backtracking(nums, 0);
return res;
}
};
// @lc code=end
90.子集II
代码随想录地址:
https://programmercarl.com/0090.%E5%AD%90%E9%9B%86II.html
题目
给你一个整数数组 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
思路
要剪枝,要存储所有节点的结果(不只是叶子节点)。
代码
/*
* @lc app=leetcode.cn id=90 lang=cpp
*
* [90] 子集 II
*/
// @lc code=start
class Solution {
private:
vector<vector<int>> res;
vector<int> path;
//要求传入的nums是排序的
void backtracking(vector<int>& nums, int start)
{
//每次都将结果插入res中
//因为是集合
res.push_back(path);
//终止条件--可以不写,因为for循环自己会结束的
if(start >= nums.size())
return;
//单层for循环
for(int i = start; i < nums.size(); i++)
{
//树层剪枝
if(i > start && nums[i] == nums[i - 1])
continue;
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
res.clear();
path.clear();
sort(nums.begin(), nums.end());
backtracking(nums, 0);
return res;
}
};
// @lc code=end