作者学习算法的教材是LeetCode 101
有需要的同学直接GitHub搜索LeetCode 101即可 **
深度优先搜索(DFS)
用到栈结构或递归,先入后出;可以用来检测是否有环路。
695. 岛屿的最大面积(难度:中等)
- 使用深度优先遍历算法dfs,用directed表示方向,每次深度优先遍历算法中都要判断当前节点的上下左右四个位置;
- 代码如下:
class Solution {
vector<int> direction{-1 , 0 , 1 , 0 , -1};
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
if(grid.empty() || grid[0].empty()) {
return 0;
}
int max_area = 0;
for(int i = 0 ; i < grid.size() ; i++) {
for(int j = 0 ; j < grid[i].size() ; j++) {
if(grid[i][j] == 1) {
max_area = max(max_area , dfs(grid , i , j));
}
}
}
return max_area;
}
int dfs(vector<vector<int>>& grid , int r , int c) {
if(grid[r][c] == 0) {
return 0;
}
grid[r][c] = 0;
int x , y , area = 1;
for(int i = 0 ; i < 4 ; i++) {
x = r + direction[i];
y = c + direction[i + 1];
if(x < grid.size() && y < grid[0].size()) {
area += dfs(grid , x , y);
}
}
return area;
}
};
547. 省份数量(难度:中等)
- 使用visited记录结点是否被访问过,接着用深度优先遍历算法dfs找到同一个省的所有城市;
- 代码如下:
class Solution {
public:
int findCircleNum(vector<vector<int>>& isConnected) {
vector<bool> visited(isConnected.size() , false);
int count = 0;
for(int i = 0 ; i < isConnected.size() ; i++) {
if(!visited[i]) {
dfs(isConnected , i ,visited);
count++;
}
}
return count;
}
void dfs(vector<vector<int>>& isConnected , int i , vector<bool>& visited) {
visited[i] = true;
for(int k = 0 ; k < isConnected.size() ; k++) {
if(isConnected[i][k] == 1 && !visited[k]) {
dfs(isConnected , k , visited);
}
}
}
};
417. 太平洋大西洋水流问题(难度:中等)
- 题目要求输出能到达大西洋和太平洋的顶点,若对所有节点进行搜索,复杂度很高,所以可以从太平洋和大西洋出发,逆着找到这些顶点;
- 分别用两个二维数组保存节点状态(分别是从太平洋出发和大西洋)出发,如果最后两个二维数组中同一个节点均为true,说明该节点可以到达太平洋和大西洋,输出该节点;
- 代码如下:
class Solution {
vector<int> direction{-1 , 0 , 1 , 0 , -1};
public:
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
if(heights.empty() || heights[0].empty()) {
return heights;
}
vector<vector<int>> ans;
vector<vector<bool>> can_reach_p(heights.size() , vector<bool>(heights[0].size() , false));
vector<vector<bool>> can_reach_a(heights.size() , vector<bool>(heights[0].size() , false));
for(int i = 0 ; i < heights.size() ; i++) {
dfs(heights , can_reach_p , i , 0);
dfs(heights , can_reach_a , i , heights[0].size() - 1);
}
for(int i = 0 ; i < heights[0].size() ; i++) {
dfs(heights , can_reach_p , 0 , i);
dfs(heights , can_reach_a , heights.size() - 1 , i);
}
for(int i = 0 ; i < heights.size() ; i++) {
for(int j = 0 ; j < heights[0].size() ; j++) {
if(can_reach_a[i][j] && can_reach_p[i][j]) {
ans.push_back(vector<int>{i , j});
}
}
}
return ans;
}
void dfs(vector<vector<int>>& heights , vector<vector<bool>>& can_reach , int r , int c) {
if(can_reach[r][c]) {
return;
}
can_reach[r][c] = true;
int x , y;
for(int i = 0 ; i < 4 ; i++) {
x = r + direction[i];
y = c + direction[i + 1];
if(x >= 0 && x < heights.size() && y >= 0 && y < heights[0].size() && heights[x][y] >= heights[r][c]) {
dfs(heights , can_reach , x ,y);
}
}
}
};
回溯法(先改变,后还原)
46. 全排列(难度:中等)
- 由于前后顺序不同算两种排列,所以递归时用level+1而不是i+1,这样可以遍历到当前节点前的数字;
- 代码如下:
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
backtracking(nums , 0 , res);
return res;
}
void backtracking(vector<int>& nums , int level , vector<vector<int>>& res) {
if(level == nums.size() - 1) {
res.push_back(nums);
return;
}
for(int i = level ; i < nums.size() ; i++) {
swap(nums[i] , nums[level]);
backtracking(nums , level + 1 , res);
swap(nums[i] , nums[level]);
}
}
};
77. 组合(难度:中等)
- 回溯是否把当前的数字加入到结果中;
- 前后顺序不同算一种组合,所以递归时用i+1而不是pos+1;
- 代码如下:
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> res;
vector<int> comb(k , 0);
int count = 0;
backtracting(res , comb , count , 1 , n , k);
return res;
}
void backtracting(vector<vector<int>>& res , vector<int>& comb , int count , int pos , int n , int k) {
if(count == k) {
res.push_back(comb);
return;
}
for(int i = pos ; i <= n ; i++) {
comb[count++] = i;
backtracting(res , comb , count , i + 1 , n , k);
count--;
}
}
};
79. 单词搜索(难度:中等)
- 使用visited记录节点是否被查看过,find记录单词是否被找到,从给定的数组中的每一个元素出发,判断是否存在给定单词;
- 代码如下:
class Solution {
public:
bool exist(vector<vector<char>>& board, string word) {
if(board.empty()) {
return false;
}
vector<vector<bool>> visited(board.size() , vector<bool>(board[0].size() , false));
bool find = false;
for(int i = 0 ; i < board.size() ; i++) {
for(int j = 0 ; j < board[0].size() ; j++) {
backtracking(board , i , j , word , 0 , find , visited);
}
}
return find;
}
void backtracking(vector<vector<char>>& board , int i , int j , string& word , int pos , bool& find , vector<vector<bool>>& visited) {
if(i < 0 || i >= board.size() || j < 0 || j >= board[0].size()) {
return;
}
if(visited[i][j] || find || board[i][j] != word[pos]) {
return;
}
if(pos == word.size() - 1) {
find = true;
return;
}
visited[i][j] = true;
backtracking(board , i , j + 1 , word , pos + 1 , find , visited);
backtracking(board , i , j - 1 , word , pos + 1 , find , visited);
backtracking(board , i + 1 , j , word , pos + 1 , find , visited);
backtracking(board , i - 1 , j , word , pos + 1 , find , visited);
visited[i][j] = false;
}
};
51. N皇后(难度:困难)
- 使用visited记录节点是否被查看过
- 代码如下:
广度优先搜索(BFS)
通过队列实现
934 . 最短的桥(难度:中等)
- 先到找第一个岛屿(即值为1的点),然后通过深度优先遍历算法找到与该点相连的所有的点,这样就找的了一个岛屿,在深度优先算法遍历的过程中,把第一个岛屿的值都变为2、把值为0的点都加入到队列points中,这样是为了与第二个岛屿区分开,数组中就只剩下了0、1、2三种数字;
- 然后遍历队列points,先记录队列的初始长度,用count记录需要改变的0的数量,遍历队列中各个点的上下左右位置,遇到值为2的点,继续循环,遇到值为1的点,说明找到了第二个岛屿,返回count,如果遇到值为0的点,将该点加入到队列中,并将该点的值改为2;(因为初始队列中都是与第一个岛屿相邻的点)
- 如果遍历完初始长度的points队列未结束,开始继续遍历points,方法与上面相同,但count要加一;
- 代码如下:
class Solution {
vector<int> direction{-1 , 0 , 1 , 0 , -1};
public:
int shortestBridge(vector<vector<int>>& A) {
int m = A.size();
int n = A[0].size();
queue<pair<int , int>> points;
bool flipped = false;
for(int i = 0 ; i < m ; i++) {
if(flipped) {
break;
}
for(int j = 0 ; j < n ; j++) {
if(A[i][j] == 1) {
dfs(points , A , m , n , i , j);
flipped = true;
break;
}
}
}
int x , y;
int count = 0;
while(!points.empty()) {
count++;
int n_points = points.size();
while(n_points--) {
auto [r , c] = points.front();
points.pop();
for(int k = 0 ; k < 4 ; k++) {
x = r + direction[k];
y = c + direction[k + 1];
if(x >= 0 && y >= 0 && x < m && y < n) {
if(A[x][y] == 2) {
continue;
}
if(A[x][y] == 1) {
return count;
}
points.push({x , y});
A[x][y] = 2;
}
}
}
}
return 0;
}
void dfs(queue<pair<int , int>>& points , vector<vector<int>>& A , int m , int n , int i , int j) {
if(i < 0 || i >= m || j < 0 || j >= n || A[i][j] == 2) {
return;
}
if(A[i][j] == 0) {
points.push({i , j});
return;
}
A[i][j] = 2;
dfs(points , A , m , n , i , j + 1);
dfs(points , A , m , n , i , j - 1);
dfs(points , A , m , n , i + 1 , j);
dfs(points , A , m , n , i - 1 , j);
}
};
126 . 单词接龙Ⅱ(难度:困难)
- 使用visited记录节点是否被查看过
- 代码如下:
130 . 被围绕的区域(难度:中等)
- 与上文中的太平洋、大西洋问题思路一致;
- 由于边上的O不会被改变,所以与边上O相连的O也不会被改变,从数组的四个边出发,找到所有从边界出发的O,标为false(不改变),最后遍历整个数组,如果遇到O且标记为true,则变为X;
- 代码如下:
class Solution {
vector<int> direction{-1 , 0 , 1 , 0 , -1};
public:
void solve(vector<vector<char>>& board) {
int m = board.size();
int n = board[0].size();
vector<vector<bool>> check(m , vector<bool>(n , true));
for(int i = 0 ; i < m ; i++) {
if(board[i][0] == 'O') {
dfs(board , check , m , n , i , 0);
}
if(board[i][n - 1] == 'O') {
dfs(board , check , m , n , i , n - 1);
}
}
for(int i = 0 ; i < n ; i++) {
if(board[0][i] == 'O') {
dfs(board , check , m , n , 0 , i);
}
if(board[m - 1][i] == 'O') {
dfs(board , check , m , n , m - 1 , i);
}
}
for(int i = 0 ; i < m ; i++) {
for(int j = 0 ; j < n ; j++) {
if(check[i][j] && board[i][j] == 'O') {
board[i][j] = 'X';
}
}
}
}
void dfs(vector<vector<char>>& board , vector<vector<bool>>& check , int m , int n , int i , int j) {
if(i < 0 || i >= m || j < 0 || j >= n) {
return;
}
if(board[i][j] != 'O') {
return;
}
if(!check[i][j]) {
return;
}
check[i][j] = false;
int x , y;
for(int k = 0 ; k < 4 ; k++) {
x = i + direction[k];
y = j + direction[k + 1];
dfs(board , check , m , n , x , y);
}
}
};
257 . 二叉树的所有路径(难度:简单)
- 深度优先遍历算法,遇到叶子节点将路径加入到返回数组res中;
- 代码如下:
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
if(root == nullptr) {
return vector<string>{};
}
vector<string> res;
out(root , res , "");
return res;
}
void out(TreeNode* root , vector<string>& res , string path) {
path += to_string(root->val);
if(root->left == nullptr && root->right == nullptr) {
res.push_back(path);
return;
}
if(root->left) {
out(root->left , res , path + "->");
}
if(root->right) {
out(root->right , res , path + "->");
}
}
};
47 . 单词接龙Ⅱ(难度:困难)
- 使用visited记录节点是否被查看过
- 代码如下:
40 . 单词接龙Ⅱ(难度:困难)
- 使用visited记录节点是否被查看过
- 代码如下:
37 . 单词接龙Ⅱ(难度:困难)
- 使用visited记录节点是否被查看过
- 代码如下:
310 . 单词接龙Ⅱ(难度:困难)
- 使用visited记录节点是否被查看过
- 代码如下: