【前言】打卡活动,白嫖讲解(b站搜大雪菜),oj用的LeetCode和AcWing 。一天5题,打卡二十一天。
【DFS+回溯】
深度搜索只是一种算法思想,不等同于递归,实现方式即可用递归也可用迭代。
1 电话号码的字母组合
题目描述
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
样例
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
笔记
for(auto u:digits)
这样的循环形式更简便,不用考虑下标等问题,不易出错
for(int i=0;i<digits.size();i++){ //等价于for(auto u:digits)
vector<string> now;
int u=digits[i]-'2';
for(int j=0;j<chars[u].size();j++){//等价于for(auto c:chars[u-'2']
char c=chars[u][j];
for(auto s:state) now.push_back(s+c);
}
state=now;
}
- 注意,有时候枚举比找映射规律更方便
- vector初始化:
vector<int> v(10,1);
数组 v 初始化10个大小,每个值置1
代码
//1 判断边界 2 数据结构 枚举数组 3循环三层
class Solution {
public:
string chars[8]={"abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
vector<string> letterCombinations(string digits) {
if(digits.empty()) return vector<string>();
vector<string> state(1,"");//定义了1个string元素的向量,且给出每个元素的初值为""
for(int i=0;i<digits.size();i++){
vector<string> now;
int u=digits[i]-'2';
for(int j=0;j<chars[u].size();j++){
char c=chars[u][j];
for(auto s:state) now.push_back(s+c);
}
state=now;
}
return state;
}
};
2 单词搜索
题目描述
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
样例
board =
[
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]
]
给定 word = “ABCCED”, 返回 true.
给定 word = “SEE”, 返回 true.
给定 word = “ABCB”, 返回 false.
笔记
- DFS 棋盘模板:
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1}; //上左下右4个方向移动,走过的不能回退,体现在 used[] 上,走过的置1
bool dfs(vector<vector<char>>& board, string& word,int x,int y,int count){
//出口 1 不满足条件无需进入函数
if(board[x][y] != word[count]) return false;
//出口 2 满足条件,按题目要求返回结果
if(count == word.size()-1) return true;
//入口
board[x][y]='.'; //直接在代替used[]
for(int i=0;i<4;i++){
int a=x+dx[i],b=y+dy[i];
if(a>=0 && a<row && b>=0 && b<col)
if(dfs(board,word,a,b,count+1)) return true;
}
board[x][y]=word[count]; //回溯
return false;
代码
/*
1 数据结构
2 算法逻辑
只有三种走法,不能回退
*/
class Solution {
public:
int r,c; //行 列
int dx[4]={-1,0,1,0},dy[4]={0,-1,0,1};
bool exist(vector<vector<char>>& board, string word) {
if(board.empty()||board[0].empty()) return false;
r=board.size(), c=board[0].size();
for(int i=0;i<r;i++){
for(int j=0;j<c;j++)
if(dfs(board,word,i,j,0)) return true;
}
return false;
}
//传参& 不需要复制数组,如果不加&,复制浪费时间
bool dfs(vector<vector<char>>& board, string& word,int x,int y,int count){
if(board[x][y]!=word[count]) return false;
if(count==word.size()-1) return true;
board[x][y]='.';
for(int i=0;i<4;i++){
int a=x+dx[i],b=y+dy[i];
if(a>=0&&a<r&&b>=0&&b<c)
if(dfs(board,word,a,b,count+1)) return true;
}
//恢复
board[x][y]=word[count];
return false;
}
};
3 全排列
题目描述
给定一个没有重复数字的序列,返回其所有可能的全排列。
样例
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
笔记
-
全排列属于递归树里面最基础的题。对题目给定vector数组 操作不熟练。
-
vector<int> abc; //初始化一个size为0的vector
vector<int> abc(10); //初始化了10个默认值为0的元素
vector<int> cde(10,1); //初始化了10个值为1的元素
int a[5] = {1,2,3,4,5}; vector<int> b(a, a+5); //地址是从0到5(左闭右开区间)
vector<int> a(5,1); vector<int> b(a);
-
path.pop_back(); // 弹出
代码
class Solution {
public:
//递归树
int n;
vector<bool> used;
vector<vector<int>> ans;
vector<int> path;
void dfs(vector<int>& nums,int count){
if(count==n) {
ans.push_back(path);
return;
}
for(int i=0;i<n;i++){
if(used[i]==0){
used[i]=1;
path.push_back(nums[i]);
dfs(nums,count+1);
path.pop_back(); //恢复现场
used[i]=0;
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
n=nums.size(); //保存大小 方便使用
used = vector<bool>(n); //vector 赋值
dfs(nums,0);
return ans;
}
};
4 全排列(含重复数字)
题目描述
给定一个可包含重复数字的序列,返回所有不重复的全排列。
样例
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]
笔记
- 含重复数字关键是要把相同数字产生的相同排列跳过——判重。先把所有相同数字放到一起,便于跳过,给dfs加一个参数 start,若后一个数相同,则从下一个位置开始遍历。如 1->5,第二个 1 只能从 位置6 开始 。为了实现 在基础模板上 增加了 sort函数,start参数
- 此题解法为:枚举每个数放到哪个位置,上题:枚举每个位置放哪个数。两种方法均可。
其中,枚举每个数放到哪个位置:path [i]=nums [count];
枚举每个位置放哪个数:path [count] = nums [i];
count表示“每个”
代码
class Solution {
public:
int n;
vector<int> used;
vector<int> path;
vector<vector<int>> ans;
vector<vector<int>> permuteUnique(vector<int>& nums) {
n=nums.size();
used=vector<int>(n);
path=vector<int>(n);
sort(nums.begin(),nums.end());
dfs(nums,0,0);
return ans;
}
void dfs(vector<int>& nums,int count,int start){
if(count==n){
ans.push_back(path);
return;
}
for(int i=start;i<n;i++){
if(used[i]==0){
used[i]=1;
path[i]=nums[count]; //与之前不同
int sta = count+1<n && nums[count+1]==nums[count]?i+1:0;
dfs(nums,count+1,sta);
used[i]=0;
}
}
}
};
5 子集
题目描述
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
样例
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
笔记
集合问题中,利用 二进制 进行移位运算是最简便的方法,例中,元素个数 为 3 ,幂集个数为 23,循环 0-8 ,用二进制表示,每位与1判断此位是否为1,若为1,输出此下标元素
代码
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ans;
for(int i=0;i< 1<<nums.size();i++){
vector<int> temp;
for(int j=0;j<nums.size();j++){
if(i>>j & 1) temp.push_back(nums[j]);
}
ans.push_back(temp);
}
return ans;
}
};