搜索(2.15) |
|
|
BFS | ||
1. 计算在网格中从原点到特定点的最短路径长度 | 修改后做出 |
|
2. 组成整数的最小平方数数量(2.16) | 一开始未做出 | ※ |
3. 最短单词路径 | 思考后做出 |
|
DFS | ||
1. 查找最大的连通面积 | 思考后做出 |
|
2. 矩阵中的连通分量数目 | 可做出Ok,解法可简化 |
|
3. 好友关系的连通分量数目 | 一开始未做出 | ※ |
4. 填充封闭区域 | 一开始未做出 | ※ |
5. 能到达的太平洋和大西洋的区域(2.17) | 一开始未做出,其实类上。 思维要灵活! | ※ |
Backtracking | ||
1. 数字键盘组合 | 一开始未做出 | ※ |
2. IP 地址划分 | 一开始未做出 | ※ |
3. 在矩阵中寻找字符串 | 思考后做出 |
|
4. 输出二叉树中所有从根到叶子的路径 | 思考后做出 |
|
5. 排列 | 思考后做出 |
|
6. 含有相同元素求排列 | 一开始未做出 | ※ |
7. 组合 | 修改后做出 | ※ |
8. 组合求和 | 修改后做出 |
|
9. 含有相同元素的组合求和 | 修改后做出 |
|
10. 1-9 数字的组合求和 | 思考后做出 |
|
11. 子集 | 可做出Ok,解法可优化 | ※ |
12. 含有相同元素求子集 | 可做出Ok |
|
13. 分割字符串使得每个部分都是回文数 | 可做出Ok |
|
14. 数独(2.18) | 一开始未做出 | ※ |
15. N 皇后 | 思考后做出 |
|
BFS
在程序实现 BFS 时需要考虑以下问题:
- 队列:用来存储每一轮遍历得到的节点;
- 标记:对于遍历过的节点,应该将它标记,防止重复遍历。
- 层数:如果需要层数,可以建立结构体
1. 计算在网格中从原点到特定点的最短路径长度
1091. Shortest Path in Binary Matrix(Medium)
https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/
2. 组成整数的最小平方数数量
279. Perfect Squares (Medium)
https://leetcode-cn.com/problems/perfect-squares/
1. 动态规划
class Solution {
public:
int numSquares(int n) {
int dp[n+1];
for(int i=0;i<=n;i++)
dp[i]=i;
for(int i=4;i<=n;i++)
for(int j=1;i-j*j>=0;j++)
if(dp[i-j*j]+1<dp[i]) dp[i]=dp[i-j*j]+1;
return dp[n];
}
};
2. BFS
class Solution {
public:
int numSquares(int n) {
vector<bool> flag(n,true);
queue<int> q;
q.push(n);
int step=0;
while(!q.empty()){
for(int i=q.size();i>0;i--){
int cur=q.front();
if(cur==0) return step;
q.pop();
for(int j=1;cur-j*j>=0;j++)
if(flag[cur-j*j]){
q.push(cur-j*j);
flag[cur-j*j]=false;
}
}
step++;
}
return -1;
}
};
3. 最短单词路径
127. Word Ladder (Medium)
https://leetcode-cn.com/problems/word-ladder/
DFS
在程序实现 DFS 时需要考虑以下问题:
- 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。
- 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。
1. 查找最大的连通面积
695. Max Area of Island (Medium)
https://leetcode-cn.com/problems/max-area-of-island/
2. 矩阵中的连通分量数目
200. Number of Islands (Medium)
https://leetcode-cn.com/problems/number-of-islands/
3. 好友关系的连通分量数目
547. Friend Circles (Medium)
https://leetcode-cn.com/problems/friend-circles/
1. DFS
非递归解法
class Solution {
public:
int findCircleNum(vector<vector<int>>& M) {
int n=M.size(),res=0;
bool visited[n];
memset(visited,false,n);
stack<int> st;
for(int i=0;i<n;i++){
if(visited[i]) continue;
res++;
visited[i]=true;
st.push(i);
while(!st.empty()){
int cur=st.top();
st.pop();
for(int j=0;j<n;j++)
if(M[cur][j]==1&&!visited[j]){
visited[j]=true;
st.push(j);
}
}
}
return res;
}
};
递归解法
class Solution {
public:
void dfs(vector<vector<int>>& M,bool visited[],int i,int n){
for(int j=0;j<n;j++)
if(M[i][j]==1&&!visited[j]){
visited[j]=true;
dfs(M,visited,j,n);
}
}
int findCircleNum(vector<vector<int>>& M) {
int n=M.size(),res=0;
bool visited[n];
memset(visited,false,n);
for(int i=0;i<n;i++)
if(!visited[i]){
res++;
dfs(M,visited,i,n);
}
return res;
}
};
2. 并查集
class Solution {
public:
int findParent(int parents[],int i){
if(parents[i]==-1) return i;
else return findParent(parents,parents[i]);
}
void Union(int parents[],int a,int b){
int fa=findParent(parents,a);
int fb=findParent(parents,b);
if(fa!=fb) parents[fa]=fb;
}
int findCircleNum(vector<vector<int>>& M) {
int n=M.size(),res=0;
int parents[n];
memset(parents,-1,n*sizeof(int));
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(M[i][j]==1) Union(parents,i,j);
for(int i=0;i<n;i++)
if(parents[i]==-1) res++;
return res;
}
};
4. 填充封闭区域
130. Surrounded Regions (Medium)
https://leetcode-cn.com/problems/surrounded-regions/
1. DFS
class Solution {
public:
int loc[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
//非递归解法
void dfs(vector<vector<char>>& board,int i,int j,int m,int n){
board[i][j]='-';
stack<pair<int,int>> st;
st.push(make_pair(i,j));
while(!st.empty()){
pair<int,int> cur=st.top();
int i=cur.first,j=cur.second;
st.pop();
for(int k=0;k<4;k++){
int x=i+loc[k][0],y=j+loc[k][1];
if(x<0||x>=m||y<0||y>=n||board[x][y]!='O') continue;
board[x][y]='-';
st.push(make_pair(x,y));
}
}
}
//递归解法
void dfs(vector<vector<char>>& board,int i,int j,int m,int n){
board[i][j]='-';
for(int k=0;k<4;k++){
int x=i+loc[k][0],y=j+loc[k][1];
if(x<0||x>=m||y<0||y>=n||board[x][y]!='O') continue;
dfs(board,x,y,m,n);
}
}
void solve(vector<vector<char>>& board) {
if(board.size()==0) return ;
int m=board.size(),n=board[0].size();
for(int i=0;i<m;i++){
if(board[i][0]=='O')
dfs(board,i,0,m,n);
if(board[i][n-1]=='O')
dfs(board,i,n-1,m,n);
}
for(int i=0;i<n;i++){
if(board[0][i]=='O')
dfs(board,0,i,m,n);
if(board[m-1][i]=='O')
dfs(board,m-1,i,m,n);
}
for(int i=0;i<m;i++)
for(int j=0;j<n;j++){
if(board[i][j]=='O')
board[i][j]='X';
else if(board[i][j]=='-')
board[i][j]='O';
}
}
};
5. 能到达的太平洋和大西洋的区域
417. Pacific Atlantic Water Flow (Medium)
https://leetcode-cn.com/problems/pacific-atlantic-water-flow/
从边界开始遍历!
class Solution {
public:
int loc[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
void dfs(vector<vector<int>>& matrix,int x,int y,int m,int n,vector<vector<bool>>& canReach){
if(canReach[x][y]) return;
canReach[x][y]=true;
for(int i=0;i<4;i++){
int xx=x+loc[i][0],yy=y+loc[i][1];
if(xx<0||xx>=m||yy<0||yy>=n||matrix[x][y]>matrix[xx][yy]) continue;
dfs(matrix,xx,yy,m,n,canReach);
}
}
vector<vector<int>> pacificAtlantic(vector<vector<int>>& matrix) {
vector<vector<int>> res;
if(matrix.size()==0) return res;
int m=matrix.size(),n=matrix[0].size();
vector<vector<bool>> canReachP(m,vector<bool>(n,false)),canReachX(m,vector<bool>(n,false));
for(int i=0;i<n;i++){
dfs(matrix,0,i,m,n,canReachP);
dfs(matrix,m-1,i,m,n,canReachX);
}
for(int i=0;i<m;i++){
dfs(matrix,i,0,m,n,canReachP);
dfs(matrix,i,n-1,m,n,canReachX);
}
int cnt=0;
for(int i=0;i<m;i++)
for(int j=0;j<n;j++){
if(canReachP[i][j]&&canReachX[i][j])
res.push_back((vector<int>{i,j}));
}
return res;
}
};
Backtracking
Backtracking(回溯)属于 DFS。
- 普通 DFS 主要用在 可达性问题 ,这种问题只需要执行到特点的位置然后返回即可。
- 而 Backtracking 主要用于求解 排列组合 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。
因为 Backtracking 不是立即返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题:
- 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;
- 但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。
1. 数字键盘组合
17. Letter Combinations of a Phone Number (Medium)
https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
class Solution {
public:
string tel[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
void dfs(string digits,string str,vector<string>& res){
if(str.size()==digits.size()){
res.push_back(str);
return;
}
string tmp=tel[digits[str.size()]-'0'];
for(auto c:tmp){
str+=c;
dfs(digits,str,res);
str.pop_back();
}
}
vector<string> letterCombinations(string digits) {
vector<string> res;
if(digits.size()==0) return res;
dfs(digits,"",res);
return res;
}
};
2. IP 地址划分
93. Restore IP Addresses(Medium)
https://leetcode-cn.com/problems/restore-ip-addresses/
class Solution {
public:
void dfs(string s,int n,string ip,vector<string>& res){
if(n==4||s.empty()){
if(s.empty()&&n==4) res.push_back(ip);
return;
}
if(s[0]=='0'){
dfs(s.substr(1),n+1,ip+s.substr(0,1)+((n!=3)?".":""),res);
return;
}
int range=(s.size()<3)?s.size():3;
for(int i=1;i<=range;i++){
int num=stoi(s.substr(0,i));
if(num>255||s.size()-i+1>3*(4-n)) break;
dfs(s.substr(i),n+1,ip+s.substr(0,i)+((n!=3)?".":""),res);
}
}
vector<string> restoreIpAddresses(string s) {
vector<string> res;
if(s.empty()) return res;
dfs(s,0,"",res);
return res;
}
};
3. 在矩阵中寻找字符串
79. Word Search (Medium)
https://leetcode-cn.com/problems/word-search/submissions/
4. 输出二叉树中所有从根到叶子的路径
257. Binary Tree Paths (Easy)
https://leetcode-cn.com/problems/binary-tree-paths/
5. 排列
46. Permutations (Medium)
https://leetcode-cn.com/problems/permutations/
6. 含有相同元素求排列
47. Permutations II (Medium)
https://leetcode-cn.com/problems/permutations-ii/
class Solution {
public:
void backTracking(vector<vector<int>>& res,vector<int>& nums,vector<int> tmp,vector<bool> flag){
if(nums.size()==tmp.size()){
res.push_back(tmp);
return;
}
for(int i=0;i<nums.size();i++){
if(i!=0&&nums[i]==nums[i-1]&&!flag[i-1]) //含相同元素写法
continue;
if(flag[i]) continue;
flag[i]=true;
tmp.push_back(nums[i]);
backTracking(res,nums,tmp,flag);
tmp.pop_back();
flag[i]=false;
}
}
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<vector<int>> res;
if(nums.size()==0) return res;
sort(nums.begin(),nums.end());
vector<bool> flag(nums.size(),false);
backTracking(res,nums,vector<int>(),flag);
return res;
}
};
7. 组合
77. Combinations (Medium)
https://leetcode-cn.com/problems/combinations/
class Solution {
public:
void backTracking(vector<vector<int>>& res,vector<int> tmp,int val,int n,int k){
if(k==0){
res.push_back(tmp);
return;
}
for(int i=val;i<=n;i++){ //组合写法,i从val开始;排序从0开始
tmp.push_back(i); //不需要flag
backTracking(res,tmp,i+1,n,k-1);
tmp.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> res;
if(k==0) return res;
backTracking(res,vector<int>(),1,n,k);
return res;
}
};
8. 组合求和
39. Combination Sum (Medium)
https://leetcode-cn.com/problems/combination-sum/
9. 含有相同元素的组合求和
40. Combination Sum II (Medium)
https://leetcode-cn.com/problems/combination-sum-ii/
10. 1-9 数字的组合求和
216. Combination Sum III (Medium)
https://leetcode-cn.com/problems/combination-sum-iii/
11. 子集
78. Subsets (Medium)
https://leetcode-cn.com/problems/subsets/
class Solution {
public:
void backTracking(vector<vector<int>>& res,vector<int>& nums,vector<int> tmp,int st,int n){
if(tmp.size()==n){
res.push_back(tmp);
}
for(int i=st;i<nums.size();i++){
tmp.push_back(nums[i]); //不需要flag
backTracking(res,nums,tmp,i+1,n);
tmp.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
for(int i=0;i<=nums.size();i++)
backTracking(res,nums,vector<int>(),0,i);
return res;
}
};
12. 含有相同元素求子集
90. Subsets II (Medium)
https://leetcode-cn.com/problems/subsets-ii/
需要flag,解法类似9
13. 分割字符串使得每个部分都是回文数
131. Palindrome Partitioning (Medium)
https://leetcode-cn.com/problems/palindrome-partitioning/
14. 数独
37. Sudoku Solver (Hard)
https://leetcode-cn.com/problems/sudoku-solver/
从最左上角的方格开始 row = 0, col = 0。直到到达一个空方格。
- 从1 到 9 迭代循环数组,尝试放置数字 d 进入 (row, col) 的格子。
- 如果数字 d 还没有出现在当前行,列和子方块中:
- 将 d 放入 (row, col) 格子中。
- 记录下 d 已经出现在当前行,列和子方块中。
- 如果这是最后一个格子row == 8, col == 8 :
意味着已经找出了数独的解。
- 否则,放置接下来的数字。
- 如果数独的解还没找到:
将最后的数从 (row, col) 移除。
class Solution {
public:
bool rowUsed[9][10]={false};
bool colUsed[9][10]={false};
bool cubeUsed[9][10]={false};
int cube(int row,int col){
return (row/3)*3+col/3;
}
bool backTracking(vector<vector<char>>& board,int row,int col){
while(row<9&&board[row][col]!='.'){
row=(col==8)?row+1:row;
col=(col==8)?0:col+1;
}
if(row==9) return true;
for(int i=1;i<=9;i++){
if(rowUsed[row][i]||colUsed[col][i]||cubeUsed[cube(row,col)][i])
continue;
board[row][col]='0'+i;
rowUsed[row][i]=colUsed[col][i]=cubeUsed[cube(row,col)][i]=true;
if(backTracking(board,row,col)) return true;
rowUsed[row][i]=colUsed[col][i]=cubeUsed[cube(row,col)][i]=false;
board[row][col]='.';
}
return false;
}
void solveSudoku(vector<vector<char>>& board) {
for(int i=0;i<9;i++)
for(int j=0;j<9;j++){
if(board[i][j]=='.') continue;
int num=board[i][j]-'0';
rowUsed[i][num]=true;
colUsed[j][num]=true;
cubeUsed[cube(i,j)][num]=true;
}
backTracking(board,0,0);
}
};
15. N 皇后
51. N-Queens (Hard)