深度优先搜索
void dfs(){
if('递归到最远位置'){
'需要执行的函数体'
return;
}
for(;;;){
if('条件'){
'执行函数';
dfs();
'考虑是否将上面函数复位';
}
}
}
547. 省份数量
class Solution {
private:
vector<bool> isvisited;
void dfs(vector<vector<int>>& isConnected ,int x){
for(int i = 0;i<isConnected.size() ;++i){
if(i !=x && isConnected[x][i] &&!isvisited[i]){
isvisited[i] = true;
dfs(isConnected,i);
}
}
return;
}
public:
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size();
isvisited.resize(n,0);
int res = 0;
for(int i = 0;i< n;++i){
if(!isvisited[i]){
isvisited[i] = true;
dfs(isConnected,i);
res++;
}
}
return res;
}
};
46. 全排列
class Solution {
private:
vector<vector<int>>ans;
vector<int>temp;
vector<int>isvisted;
void dfs(vector<int>& nums,int x ,int n){
if(x == n){
ans.push_back(temp);
return;
}
for(int i = 0;i< n;++i){
if(!isvisted[i]){
isvisted[i] = 1;
temp.push_back(nums[i]);
dfs(nums,x+1,n);
isvisted[i] = 0;
temp.pop_back();
}
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
int n = nums.size();
isvisted.resize(n,0);
dfs(nums,0,n);
return ans;
}
};
51. N 皇后
class Solution {
public:
static const int M =20;
int row[M], col[M], dg[M] ,udg[M];
vector<string> a;
vector<vector<string>> res;
vector<vector<string>> solveNQueens(int n) {
a.resize(n,string(n,'.'));
dfs(0,n);
return res;
}
void dfs(int u ,int n){
if(u == n){
res.push_back(a);
}
for(int i = 0;i<n ;++i){
if(!row[u]&& !col[i] &&!dg[u-i+n]&&!udg[i+u]){
a[u][i] = 'Q';
row[u] = col[i] = dg[u-i+n] = udg[i+u] = 1;
dfs(u+1,n);
a[u][i] = '.';
row[u] = col[i] = dg[u-i+n] = udg[i+u] = 0;
}
}
}
};
210. 课程表 II(拓扑排序)
错误解法(无法判断出是否有环)
class Solution {
private:
vector<int> isvisited;
stack<int> st;
void dfs(vector<vector<int>>& prerequisites , int pos){
for(int i = 0 ;i< prerequisites.size() ;++i){
int y = prerequisites[i][0];
int x = prerequisites[i][1];
if(x == pos && !isvisited[y]){
isvisited[y] = 1;
dfs(prerequisites,y);
st.push(y);
}
}
}
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
vector<int> res;
int n = prerequisites.size();
isvisited.resize(numCourses,0);
for(int i = 0;i<numCourses ;++i){
if(!isvisited[i]){
dfs(prerequisites,i);
isvisited[i] = 1;
}
}
for(int i = 0;i<numCourses ;++i){
if(!isvisited[i]){
st.push(i);
}
}
if(numCourses != st.size()){
return res;
}
while(!st.empty()){
res.push_back(st.top());
st.pop();
}
return res;
}
};
需要增加一个操作来判断是否存在环
//深度优先算法
//关键在于如何判断是否存在环,将当前的访问状态设置为三类,访问中、访问前、访问后。当一个数组已经访问完成后仍然处于访问中则可以判断存在环。
//增加一个额外空间,将有向图的关系一一保存,这样在dfs中可以有效减枝
class Solution {
private:
vector<vector<int>> edges;
vector<int> isvisited;
vector<int> res;
bool vaild = true;
public:
void dfs(int u){
isvisited[u] = 1;
for(int v :edges[u]){
if(isvisited[v] == 0){
dfs(v);
if(!vaild){
return;
}
}else if(isvisited[v] == 1){
vaild = false;
return;
}
}
isvisited[u] = 2;
res.push_back(u);
}
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
edges.resize(numCourses);
isvisited.resize(numCourses,0);
for(auto a: prerequisites){
edges[a[1]].push_back(a[0]);
}
for(int i = 0;i< numCourses&&vaild ;++i){
if(!isvisited[i]){
dfs(i);
}
}
if(!vaild){
return {};
}else{
reverse(res.begin(),res.end());
return res;
}
}
};
//广度优先算法
class Solution {
private:
vector<vector<int>> edges;
vector<int> indeg;
vector<int> res;
public:
vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
edges.resize(numCourses);
indeg.resize(numCourses,0);
for(auto a: prerequisites){
edges[a[1]].push_back(a[0]);
//记录每个节点的度,即它作为前置条件的次数
++indeg[a[0]];
}
queue<int> q;
//首先将度为0的入队,他们不是任何课程的的前置条件
for(int i= 0; i<numCourses ;++i){
if(indeg[i] == 0){
q.push(i);
}
}
while(!q.empty()){
int u = q.front();
q.pop();
res.push_back(u);
for(int v:edges[u]){
indeg[v]--;
if(indeg[v] == 0){
q.push(v);
}
}
}
//环内的元素不会进入队列中
if(res.size() != numCourses){
return {};
}else{
return res;
}
}
};
684. 冗余连接(并查集)
并查集通用公式
vector<int>parent(n+1);
//并查集初始化
void init(){
for(int i = 0;i<= n;++i){
parent[i] = i;
}
}
//并查集寻根过程
int Find(vector<int>& parent , int x){
if(parent[x]!=x){
parent[x] = Find(parent, parent[x]);
}
}
//将边加入并查集
void Union(vector<int> &parent , int x1 ,int x2){
parent[Find(parent,x1)] = Find(parent , x2);
}
//判断根是否相同
bool same(vector<int> &parent , int x1 ,int x2){
return Find(parent,x1) == Find(parent,x2);
}
class Solution {
public:
int Find(vector<int>& parent, int index) {
if (parent[index] != index) {
parent[index] = Find(parent, parent[index]);
}
return parent[index];
}
void Union(vector<int>& parent, int index1, int index2) {
parent[Find(parent, index1)] = Find(parent, index2);
}
vector<int> findRedundantConnection(vector<vector<int>>& edges) {
int nodesCount = edges.size();
vector<int> parent(nodesCount + 1);
for (int i = 1; i <= nodesCount; ++i) {
parent[i] = i;
}
for (auto& edge: edges) {
int node1 = edge[0], node2 = edge[1];
if (Find(parent, node1) != Find(parent, node2)) {
Union(parent, node1, node2);
} else {
return edge;
}
}
return vector<int>{};
}
};
1254. 统计封闭岛屿的数目
class Solution {
private:
vector<vector<int>> isvisited;
int n,m;
bool flag = true;
void dfs(vector<vector<int>>& grid , int x ,int y){
if(isvisited[x][y]){
return;
}
if(x == 0||y ==0 ||x == n-1 ||y == m-1){
flag = false;
}
isvisited[x][y] = 1;
if(x+1 < n && grid[x+1][y] =
= 0) dfs(grid,x+1,y);
if(x-1 >=0 && grid[x-1][y] == 0) dfs(grid,x-1,y);
if(y+1 < m && grid[x][y+1] == 0) dfs(grid,x,y+1);
if(y-1 >= 0 && grid[x][y-1] == 0) dfs(grid,x,y-1);
}
public:
int closedIsland(vector<vector<int>>& grid) {
n = grid.size();
m = grid[0].size();
int res = 0;
isvisited.resize(n,vector<int>(m,0));
for(int i = 1;i< n-1;++i){
for(int j = 1;j< m-1 ;++j){
if(grid[i][j] == 0&&!isvisited[i][j]){
dfs(grid,i,j);
if(flag){
res++;
}
flag = true;
}
}
}
return res;
}
};
22. 括号生成
class Solution {
public:
vector<string> ans;
string temp = "";
void dfs(int n , int x ,int y){
if(temp.size() == 2*n){
ans.push_back(temp);
return;
}
if(x < n){
temp+='(';
dfs(n,x+1,y);
temp.pop_back();
}
if(y< x){
temp+=')';
dfs(n,x,y+1);
temp.pop_back();
}
}
vector<string> generateParenthesis(int n) {
dfs(n,0,0);
return ans;
}
};
77. 组合
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
方法一:直接暴力回溯(错误)
//下面代码是获得k个数的全排列代码
class Solution {
private:
vector<vector<int>>ans;
vector<int> temp;
vector<int>isvisited;
public:
void dfs(int n ,int k , int t,int pos){
if(t == k){
ans.push_back(temp);
}
for(int i = 1;i<= n;++i){
if(!isvisited[i]&&i!=pos){
temp.push_back(i);
isvisited[i] = 1;
t++;
dfs(n,k,t,i);
temp.pop_back();
isvisited[i] = 0;
t--;
}
}
}
vector<vector<int>> combine(int n, int k) {
if(k>n||k == 0) return ans;
isvisited.resize(n+1);
for(int i = 1;i<=n ;++i){
isvisited[i] = 1;
temp.push_back(i);
dfs(n,k,1,i);
isvisited[i] = 0;
temp.pop_back();
}
return ans;
}
};
方法二:剪枝
考虑到是全排列,[1,3,2]和[1,3,2]是一样的情况,所以采用剪枝方案
//仅需将方法一中回溯的for循环起点改成当前位置
class Solution {
private:
vector<vector<int>>ans;
vector<int> temp;
vector<int>isvisited;
public:
void dfs(int n ,int k , int t,int pos){
if(t == k){
ans.push_back(temp);
}
for(int i = pos;i<= n;++i){
if(!isvisited[i]&&i!=pos){
temp.push_back(i);
isvisited[i] = 1;
t++;
dfs(n,k,t,i);
temp.pop_back();
isvisited[i] = 0;
t--;
}
}
}
vector<vector<int>> combine(int n, int k) {
if(k>n||k == 0) return ans;
isvisited.resize(n+1);
for(int i = 1;i<=n ;++i){
isvisited[i] = 1;
temp.push_back(i);
dfs(n,k,1,i);
isvisited[i] = 0;
temp.pop_back();
}
return ans;
}
};
方法三:优化方法二
本题中由于下一个递归起点为pos+1,所以isvisited不需要
class Solution {
private:
vector<vector<int>>ans;
vector<int> temp;
public:
void dfs(int n ,int k , int t,int pos){
if(t == k){
ans.push_back(temp);
return;
}
for(int i = pos+1;i<= n;++i){
temp.push_back(i);
t++;
dfs(n,k,t,i);
temp.pop_back();
t--;
}
}
vector<vector<int>> combine(int n, int k) {
if(k>n||k == 0) return ans;
for(int i = 1;i<=n ;++i){
temp.push_back(i);
dfs(n,k,1,i);
temp.pop_back();
}
return ans;
}
};
78. 子集
方法一:采取与题77完全一致的解法
区别在于该解法增加了一个循环来计算k = 0到k =n 的全部情况
class Solution {
private:
vector<vector<int>>ans;
vector<int>temp;
public:
void dfs(vector<int>& nums,int k ,int t,int pos){
if(t == k){
ans.push_back(temp);
return;
}
for(int i = pos+1 ; i<nums.size() ;++i){
temp.push_back(nums[i]);
t++;
dfs(nums,k,t,i);
temp.pop_back();
t--;
}
}
vector<vector<int>> subsets(vector<int>& nums) {
int n = nums.size();
ans.push_back(temp);
for(int i = 1;i<= n ;++i){
for(int j = 0;j< n ;++j){
temp.push_back(nums[j]);
dfs(nums,i,1,j);
temp.pop_back();
}
}
return ans;
}
};
方法二:二进制枚举
class Solution {
public:
vector<int> t;
vector<vector<int>> ans;
void dfs(int cur, vector<int>& nums) {
if (cur == nums.size()) {
ans.push_back(t);
return;
}
//选择当前的数
t.push_back(nums[cur]);
dfs(cur + 1, nums);
t.pop_back();
//跳过当前的数
dfs(cur + 1, nums);
}
vector<vector<int>> subsets(vector<int>& nums) {
dfs(0, nums);
return ans;
}
};
130. 被围绕的区域
方法一:暴力DFS
class Solution {
private:
vector<vector<int>> isvisited;
void dfs(vector<vector<char>>& board , int x ,int y){
if(x-1>=0&&board[x-1][y] == 'O'&&!isvisited[x-1][y]){
isvisited[x-1][y] = 1;
dfs(board,x-1,y);
}
if(x+1<board.size()&&board[x+1][y] == 'O'&&!isvisited[x+1][y]){
isvisited[x+1][y] = 1;
dfs(board,x+1,y);
}
if(y-1>=0&&board[x][y-1] == 'O'&&!isvisited[x][y-1]){
isvisited[x][y-1] = 1;
dfs(board,x,y-1);
}
if(y+1<board[0].size()&&board[x][y+1] == 'O'&&!isvisited[x][y+1]){
isvisited[x][y+1] = 1;
dfs(board,x,y+1);
}
}
public:
void solve(vector<vector<char>>& board) {
int n = board.size();
int m = board[0].size();
if(n <=2 ||m <= 2){
return;
}
isvisited.resize(n,vector<int>(m,0));
for(int i = 0;i<n;++i){
if(board[i][0] == 'O'){
isvisited[i][0] = 1;
dfs(board,i,0);
}
if(board[i][m-1] == 'O'){
isvisited[i][m-1] = 1;
dfs(board,i,m-1);
}
}
for(int i = 1;i< m; ++i){
if(board[0][i] == 'O'){
isvisited[0][i] = 1;
dfs(board,0,i);
}
if(board[n-1][i] == 'O'){
isvisited[n-1][i] = 1;
dfs(board,n-1,i);
}
}
for(int i = 1;i< n-1 ;++i){
for(int j = 1;j< m-1 ;++j){
if(board[i][j] == 'O'&&isvisited[i][j] == 0){
board[i][j] = 'X';
}
}
}
return;
}
};
方法2优化空间复杂度
class Solution {
private:
void dfs(vector<vector<char>>& board , int x ,int y){
if(x<0||x>=board.size()||y<0||y>=board[0].size()||board[x][y]!='O'){
return;
}
board[x][y] = 'A';
dfs(board,x-1,y);
dfs(board,x+1,y);
dfs(board,x,y-1);
dfs(board,x,y+1);
}
public:
void solve(vector<vector<char>>& board) {
int n = board.size();
int m = board[0].size();
if(n <=2 ||m <= 2){
return;
}
for(int i = 0;i<n;++i){
dfs(board,i,0);
dfs(board,i,m-1);
}
for(int i = 1;i< m; ++i){
dfs(board,0,i);
dfs(board,n-1,i);
}
for(int i = 0;i< n ;++i){
for(int j = 0;j< m ;++j){
if(board[i][j] == 'O'){
board[i][j] = 'X';
}
else if(board[i][j] == 'A'){
board[i][j] = 'O';
}
}
}
return;
}
};
200. 岛屿数量
方法一:暴力DFS
class Solution {
private:
vector<vector<int>> isvisited;
void dfs(vector<vector<char>>& grid ,int x ,int y){
if(x<0||y<0||x>=grid.size()||y>=grid[0].size()||grid[x][y] == '0'||isvisited[x][y] == 1){
return;
}
isvisited[x][y] = 1;
dfs(grid,x-1,y);
dfs(grid,x+1,y);
dfs(grid,x,y-1);
dfs(grid,x,y+1);
}
public:
int numIslands(vector<vector<char>>& grid) {
int n = grid.size();
int m = grid[0].size();
int num = 0;
isvisited.resize(n,vector<int>(m,0));
for(int i = 0;i< n ;++i){
for(int j = 0; j< m ;++j){
if(grid[i][j] == '1'&&!isvisited[i][j]){
num++;
dfs(grid,i,j);
}
}
}
return num;
}
};
方法二:优化空间复杂度
class Solution {
private:
void dfs(vector<vector<char>>& grid ,int x ,int y){
if(x<0||y<0||x>=grid.size()||y>=grid[0].size()||grid[x][y] != '1'){
return;
}
grid[x][y] = '2';
dfs(grid,x-1,y);
dfs(grid,x+1,y);
dfs(grid,x,y-1);
dfs(grid,x,y+1);
}
public:
int numIslands(vector<vector<char>>& grid) {
int n = grid.size();
int m = grid[0].size();
int num = 0;
for(int i = 0;i< n ;++i){
for(int j = 0; j< m ;++j){
if(grid[i][j] == '1'){
num++;
dfs(grid,i,j);
}
}
}
return num;
}
};
1020. 飞地的数量
方法一:暴力dfs
class Solution {
private:
int res = 0;
void dfs(vector<vector<int>>& grid ,int x ,int y){
if(x<0||y<0||x>=grid.size()||y>=grid[0].size()||grid[x][y]!=1){
return;
}
res++;
grid[x][y] = 2;
dfs(grid,x-1,y);
dfs(grid,x+1,y);
dfs(grid,x,y-1);
dfs(grid,x,y+1);
}
public:
int numEnclaves(vector<vector<int>>& grid) {
int n = grid.size();
int m = grid[0].size();
if(n<=2||m<=2){
return 0;
}
for(int i = 0;i< n ;++i){
if(grid[i][0] == 1) dfs(grid,i,0);
if(grid[i][m-1] == 1) dfs(grid,i,m-1);
}
for(int i = 1;i< m ;++i){
if(grid[0][i] == 1) dfs(grid,0,i);
if(grid[n-1][i] == 1) dfs(grid,n-1,i);
}
res = 0;
for(int i = 1;i<n-1 ;++i){
for(int j = 1;j< m-1 ;++j){
if(grid[i][j] == 1){
dfs(grid,i,j);
}
}
}
return res;
}
};
方法二:优化
class Solution {
private:
void dfs(vector<vector<int>>& grid ,int x ,int y){
if(x<0||y<0||x>=grid.size()||y>=grid[0].size()||grid[x][y]!=1){
return;
}
grid[x][y] = 2;
dfs(grid,x-1,y);
dfs(grid,x+1,y);
dfs(grid,x,y-1);
dfs(grid,x,y+1);
}
public:
int numEnclaves(vector<vector<int>>& grid) {
int n = grid.size();
int m = grid[0].size();
if(n<=2||m<=2){
return 0;
}
for(int i = 0;i< n ;++i){
if(grid[i][0] == 1) dfs(grid,i,0);
if(grid[i][m-1] == 1) dfs(grid,i,m-1);
}
for(int i = 1;i< m ;++i){
if(grid[0][i] == 1) dfs(grid,0,i);
if(grid[n-1][i] == 1) dfs(grid,n-1,i);
}
int res = 0;
for(int i = 1;i<n-1 ;++i){
for(int j = 1;j< m-1 ;++j){
if(grid[i][j] == 1){
res++;
}
}
}
return res;
}
};
417. 太平洋大西洋水流问题
class Solution {
private:
int check1 = 0;
int check2 = 0;
vector<vector<int>> isvisited;
vector<vector<int>> ways = {{0,-1},{0,1},{-1,0},{1,0}};
void dfs(vector<vector<int>>& heights ,int x ,int y){
if(check1 == 1&&check2 == 1){
return;
}
if(x == 0||y == 0){
check1 = 1;
}
if(x == heights.size()-1||y == heights[0].size()-1){
check2 = 1;
}
for(int i = 0;i< 4; ++i){
int x1 = x+ways[i][0];
int y1 = y+ways[i][1];
if(x1>=0 &&y1>=0 && x1<heights.size()&& y1<heights[0].size()&&heights[x1][y1] <= heights[x][y]&&!isvisited[x1][y1]){
isvisited[x1][y1] = 1;
dfs(heights,x1,y1);
isvisited[x1][y1] = 0;
}
}
}
public:
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
int n = heights.size();
int m = heights[0].size();
vector<vector<int>> ans;
isvisited.resize(n,vector<int>(m,0));
for(int i = 0;i< n ;++i){
for(int j = 0;j< m ;++j){
isvisited[i][j] = 1;
dfs(heights,i,j);
isvisited[i][j] = 0;
if(check1==1 && check2==1){
ans.push_back({i,j});
}
check1 = check2 = 0;
}
}
return ans;
}
};
329. 矩阵中的最长递增路径
该题中记忆化搜索是需要存储到当前位置下最大的路径长度
class Solution {
private:
static constexpr int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int rows, columns;
int dfs(vector< vector<int> > &matrix, int x, int y, vector< vector<int> > &memo) {
if (memo[x][y] != 0) {
return memo[x][y];
}
++memo[x][y];
for (int i = 0; i < 4; ++i) {
int x1 = x + dirs[i][0], y1 = y + dirs[i][1];
if (check(x1,y1) && matrix[x1][y1] > matrix[x][y]) {
memo[x][y] = max(memo[x][y], dfs(matrix, x1, y1, memo) + 1);
}
}
return memo[x][y];
}
bool check(int x ,int y){
return x>=0&&y>=0&&x<rows&&y<columns;
}
public:
int longestIncreasingPath(vector< vector<int> > &matrix) {
if (matrix.size() == 0 || matrix[0].size() == 0) {
return 0;
}
rows = matrix.size();
columns = matrix[0].size();
vector<vector<int>>memo (rows, vector <int> (columns,0));
int ans = 0;
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < columns; ++j) {
ans = max(ans, dfs(matrix, i, j, memo));
}
}
return ans;
}
};
1631. 最小体力消耗路径
//超时代码
class Solution {
private:
int maxres = INT_MAX;
int n,m;
int temp = 0;
vector<vector<int>> ways = {{0,-1},{0,1},{-1,0},{1,0}};
vector<vector<int>> isvisited;
void dfs(vector<vector<int>>& heights ,int x ,int y){
if(x == n-1 &&y == m-1){
maxres = min(temp,maxres);
return;
}
for(int i = 0;i< 4;++i){
int x1 = x+ways[i][0];
int y1 = y+ways[i][1];
if(x1>=0&&y1>=0&&x1<n&&y1<m&&!isvisited[x1][y1]){
isvisited[x1][y1] = 1;
int u = temp;
temp=max(abs(heights[x1][y1] - heights[x][y]),temp);
dfs(heights,x1,y1);
isvisited[x1][y1] = 0;
temp=u;
}
}
}
public:
int minimumEffortPath(vector<vector<int>>& heights) {
n = heights.size();
m = heights[0].size();
isvisited.resize(n, vector<int>(m,0));
isvisited[0][0] = 1;
dfs(heights,0,0);
return maxres;
}
};
class Solution {
private:
int n,m;
int flag = false;
vector<vector<int>> ways = {{0,-1},{0,1},{-1,0},{1,0}};
bool dfs(vector<vector<int>>& heights,int target,int x ,int y,vector<vector<int>>&isvisited){
if(x == n-1 &&y == m-1){
return true;
}
isvisited[x][y] = 1;
for(int i = 0;i< 4;++i){
int x1 = x+ways[i][0];
int y1 = y+ways[i][1];
if(x1>=0&&y1>=0&&x1<n&&y1<m&&!isvisited[x1][y1]&&abs(heights[x1][y1] - heights[x][y])<=target){
if(dfs(heights,target,x1,y1,isvisited)){
return true;
};
}
}
return false;
}
public:
int minimumEffortPath(vector<vector<int>>& heights) {
n = heights.size();
m = heights[0].size();
int left = 0, right = 999999;
while(left<right){
int mid = (left+right)>>1;
vector<vector<int>> isvisited(n, vector<int>(m,0));
isvisited[0][0] = 1;
if(dfs(heights,mid,0,0,isvisited)){
right = mid;
}else{
left = mid+1;
}
}
return left;
}
};
本文总结了LeetCode中涉及深度优先搜索(DFS)的算法题,包括547. 省份数量、46. 全排列、51. N 皇后等,并详细介绍了常见解题方法,如拓扑排序、并查集、剪枝优化等,同时分析了暴力DFS与优化空间复杂度的策略。

702

被折叠的 条评论
为什么被折叠?



