学习目标:
博主介绍: 27dCnc
专题 : 数据结构帮助小白快速入门
👍👍👍👍👍👍👍👍👍👍👍👍
☆*: .。. o(≧▽≦)o .。.:*☆
学习时间:
- 周一至周五晚上 7 点—晚上9点
- 周六上午 9 点-上午 11 点
- 周日下午 3 点-下午 6 点
主题: 回溯算法
今日份打卡
- 代码随想录-回溯算法
学习内容:
- 复原IP地址
- 子集
- 子集II
内容详细
93.复原IP地址
题目考点 : IP基础知识
回溯
条件判断
隔板法
题目分析: 本题也是查用隔板法将各个数据隔开,然后判断每个隔开的数据最后得到答案
判断子串是否合法
最后就是在写一个判断段位是否是有效段位了。
主要考虑到如下三点:
- 段位以0为开头的数字不合法
- 段位里有非正整数字符不合法
- 段位如果大于255了不合法
回溯三部曲
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
详细请看文章 : 回溯算法基础
最后代码
class Solution {
vector<string>result;
void backtricking(string&s,int StartIndex,int poinSum) { //pointSum是逗点的统计
if(poinSum == 3) { // 终止条件
if(isGood(s, StartIndex, s.size() - 1)) {
result.push_back(s);
}
return ;
}
for(auto i = StartIndex; i < s.size();i++) {
if(isGood(s,StartIndex,i)) {
s.insert(s.begin() + i + 1, '.');
poinSum++;
backtricking(s, i+2, poinSum);
poinSum--;
s.erase(s.begin() + i + 1);
} else break;
}
}
bool isGood(const string& s, int start, int end) {
if (start > end) {
return false;
}
if (s[start] == '0' && start != end) {
return false;
}
int num = 0;
for(auto i = start; i<= end; i++) {
if (s[i] > '9' || s[i] < '0') {
return false;
}
num = num * 10 + (s[i] - '0');
if (num > 255) {
return false;
}
}
return true;
}
public:
vector<string> restoreIpAddresses(string s) {
result.clear();
if (s.size() < 4 || s.size() > 12) return result;
backtricking(s,0,0);
return result;
}
};
78.子集
题目考点: 集合子集
组合
回溯收集路径
解题思路
如果把子集问题、组合问题、分割问题都抽象为一棵树 的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!
其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。
那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!
所以运到集合问题时我们用 startIndex
去控制我们遍历顺序,遍历的位置开始,
题目回溯图
题解代码
class Solution {
private:
vector<vector<int>> result;
vector<int>v;
public:
void backtracking(vector<int>&nums,int startIndex) {
result.push_back(v); //收集路径
if (startIndex >= nums.size()) {
return ;
}
for(auto i = startIndex;i < nums.size(); ++i) {
v.push_back(nums[i]);
backtracking(nums,i+1);
v.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
result.clear(),v.clear();
backtracking(nums,0);
return result;
}
};
90.子集II
题目考点: 回溯
子集
回溯路径收集
回溯去重
题目思路
这个题目考点便是, 子集可以按照任意顺序排列
这个说明我们取的元素不可以重复,但是我们的集合也不能重复,这个便是题目的考点,我们可以取同一个元素多次,但是最后组合成的结果不能出现重复,这个时候我们就要取重,关于去重方法我们可以采用标记法
关键图解
最后题目代码
class Solution {
private:
vector<vector<int>>result;
vector<int>vec;
public:
void backtracking(vector<int>&nums,int StratIndex,vector<bool>& used) {
result.push_back(vec);
// used[i-1] ==true,说明同一树枝candidates[i-1]使用过
// used[i-1] ==false,说明同一树层candidates[i-1]使用过
//而我们要对同一树层使用过的元素进行跳过
for(auto i = StratIndex; i < nums.size(); ++i) {
if (i>0 && nums[i] == nums[i-1] && used[i-1] == false) {
continue;
}
vec.push_back(nums[i]);
used[i] = true;
backtracking(nums,i+1,used);
used[i] = false;
vec.pop_back();
}
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
result.clear(),vec.clear();
vector<bool> used(nums.size(),false);
sort(nums.begin(),nums.end());
backtracking(nums,0,used);
return result;
}
};
使用set去重的方法
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex) {
result.push_back(path);
unordered_set<int> uset;
for (int i = startIndex; i < nums.size(); i++) {
if (uset.find(nums[i]) != uset.end()) {
continue;
}
uset.insert(nums[i]);
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
result.clear();
path.clear();
sort(nums.begin(), nums.end()); // 去重需要排序
backtracking(nums, 0);
return result;
}
关于去重
去重的方法有很多但是我们最后和目的只要一个,怎么简单怎么来
也可以这样写
if (i > startIndex && nums[i] == nums[i - 1] ) {
continue;
}
题型总结
学习产出:
- 技术笔记 2 遍
- CSDN 技术博客 3 篇
- 习的 vlog 视频 1 个
🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~