Leetcode题解-算法-搜索

1、BFS

1.1 将一个数分解为整数的平方和

279. Perfect Squares(Medium)
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, …) which sum to n.
Example 1:

Input: n = 12
Output: 3
Explanation: 12 = 4 + 4 + 4.

Example 2:

Input: n = 13
Output: 2
Explanation: 13 = 4 + 9.

方法一:BFS
计算最少可以拆解为几个平方和,用广度优先搜索,如果两个数的差等于整数的平方,则认为这两个数之间有边,问题转化为求整数 n 到整数 0 之间的最短路径。

定义一个队列,存放每层遍历的数字,再定义一个数组,查看每个节点(这里就是数字)是否遍历过,只走没走过的路径。

class Solution {
public:
    int numSquares(int n) {
        vector<int>square = generate(n);
        queue<int>qu;
        vector<int>visited(n+1, 0);
        qu.push(n);
        visited[n] = 1;
        int step = 0;
        while (!qu.empty()){
            int len = qu.size();
            step++;
            for(int i = 0; i < len; i++){
                int fr = qu.front();
                qu.pop();
                for(int c : square){
                    int next = fr - c;
                    if(next < 0)
                        break;
                    if(next == 0)
                        return step;
                    if(visited[next])
                        continue;
                    qu.push(next);
                    visited[next] = 1;
                }
            }
        }
        return n;
    }
private:
    vector<int>generate(int n){
        vector<int>res;
        for (int i = 1; i*i <= n; i++)
            res.push_back(i *i);
        return res;
    }
};

方法二:动态规划
子问题:i 最少能用几个数的平方和表示,用 dp[i] 表示
初始化:dp[i] = i,即用 1 的平方和表示
递推条件:dp[i] = min(dp[i], dp[i-j*j] + 1)

class Solution {
public:
    int numSquares(int n) {
        vector<int>dp(n+1, 0);
        for (int i = 0; i <= n; i++)
            dp[i] = i;
        for (int i = 1; i <= n; i++){
            for (int j = 1; j*j <= i; j++)
                dp[i] = min(dp[i], dp[i-j*j] + 1);
        }
        return dp[n];
    }
};

1.2 最短单词路径

127. Word Ladder(Medium)
给一个出发单词和一个结束单词,和一个单词序列,每次只能变化一个字母,问最快几个可以把出发单词变成结束单词。

Example 1:

Input:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
Output: 5
Explanation: As one shortest transformation is “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
return its length 5.

Example 2:

Input:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
Output: 0
Explanation: The endWord “cog” is not in wordList, therefore no possible transformation.

解题思路:广度优先搜索
将每个单词看成节点,只有一个字母不相同的单词之间存在边。

先将出发单词放入队列中,每次从队列中取出一个元素,如果取出的元素等于结束元素,就找到了,否则将所有没有访问过的,之差一个字符的单词放入队列中。

class Solution {
public:
    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        int n = wordList.size();
        vector<int>visited(n, 0);
        queue<string>qu;
        qu.push(beginWord);
        int step = 0;
        while (!qu.empty()) {
            int len = qu.size();
            step++;								//更新层数
            for (int i = 0; i < len; i++){ 		//取出队列的每个元素
                string fr = qu.front();
                qu.pop();
                if (fr == endWord) return step;
                for (int j = 0; j < n; j++){ 	//没访问过的,存在边的节点放入队列中
                    if (visited[j] == 0 && diff(fr, wordList[j]) == 1){
                        qu.push(wordList[j]);
                        visited[j] = 1;			//访问过的元素做标记
                    }
                }
            }            
        }
        return 0;
    }
    int diff(string s, string t){ 				//查看两个单词之间有几个字母不相同
        int cnt = 0;
        for (int i = 0; i < s.size(); i++){
            if (s[i] != t[i])
                cnt++;
        }
        return cnt;
    }
    
};

1.3 K 站中转内最便宜的航班

787. Cheapest Flights Within K Stops(Medium)
Example 1:

Input:
n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]]
src = 0, dst = 2, k = 1
Output: 200
Explanation:
The graph looks like this:
The cheapest price from city 0 to city 2 with at most 1 stop costs 200, as marked red in the picture.
在这里插入图片描述

class Solution {
public:
    int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int K) {
        queue<vector<int>>q;            //队列中每个vector的第一个元素为该路径的花的钱,后面的元素表示路径
        q.push(vector<int>{0, src});
        while (!q.empty()) {
            auto tmp = q.front();
            q.pop();
            if(tmp[tmp.size()-1] == dst) {      //找到目的节点
                if (tmp.size() -1 <= K+2)       //不算起点终点,中间点个数不能小于等于K
                    min_value = min(min_value, tmp[0]); //更新最少钱数
            }
            if(tmp.size() > K+2 || tmp[0] > min_value)  //路径超出的,钱大于给定值的,直接丢弃
                continue;
            for(auto key : flights){
                //找到的从该起点的路径,且终点没有访问过(用find返回尾迭代器表示节点未访问)更新钱数,更新路径
                if(key[0] == tmp[tmp.size() - 1] && find(tmp.begin() + 1, tmp.end(), key[1]) ==tmp.end()){
                    vector<int>new_tmp;
                    new_tmp.insert(new_tmp.begin(), tmp.begin(), tmp.end());//新的价值,路径移动到新容器中
                    new_tmp[0] += key[2];
                    new_tmp.push_back(key[1]);
                    q.push(new_tmp);
                }
            }
        }
        if(min_value == INT_MAX)
            return -1;
        return min_value;
    }
private:
    int min_value = INT_MAX;
};

2、DFS

2.1 查找最大的连通面积

695. Max Area of Island(Medium)
Example 1:

[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
Given the above grid, return 6. Note the answer is not 11, because the island must be connected 4-directionally.

Example 2:

[[0,0,0,0,0,0,0,0]]
Given the above grid, return 0.

class Solution {
public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        if (grid.empty() || grid[0].empty()) return 0;
        row = grid.size();
        col = grid[0].size();
		int res = 0;
		for (int i = 0; i < row; i++)	{
			for (int j = 0; j< col; j++) {
				if (grid[i][j] == 1) {
					int area = 0;
					DFS(grid, area, i, j);
					res = max(res, area);
				}
			}
		}
		return res;
    }
    void DFS(vector<vector<int>>& grid, int &area, int x, int y) {
		if(x < 0 || x >= row || y < 0 || y >= col || grid[x][y] == 0)
            return;
		grid[x][y]= 0;
		area++;
		DFS(grid,area,x,y+1);
		DFS(grid,area,x,y-1);
		DFS(grid,area,x-1,y);
		DFS(grid,area,x+1,y);
	}
private:
    int row;
    int col;
};

2.2 矩阵中的连通分量数目

200. Number of Islands(Medium)
Example 1:

Input:
11110
11010
11000
00000
Output: 1

Example 2:

Input:
11000
11000
00100
00011
Output: 3

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        if(grid.size() == 0 || grid[0].size() == 0)
            return 0;
        row = grid.size();
        col = grid[0].size();
        int res = 0;
        for(int i = 0; i < row; i++){
            for(int j = 0; j < col; j++){
                if(grid[i][j] == '1'){
                    DFS(grid, i, j);
                    res++;
                }
            }  
        }
        return res;
    }
    void DFS(vector<vector<char>>& grid, int i, int j){
        if(i < 0 || i >= row || j < 0 || j >= col || grid[i][j] == '0')
            return;
        grid[i][j] = '0';			//修改访问过的节点
        DFS(grid, i + 1, j);
        DFS(grid, i - 1, j);
        DFS(grid, i, j + 1);
        DFS(grid, i, j - 1);
    }
    
private:
    int row;
    int col;
};

2.3 朋友圈的数量

547. Friend Circles(Medium)
矩阵 M 中的 M[i][j] == 1 表示 i 和 j 是朋友,直接朋友和间接朋友组成一个朋友圈,问有多少个朋友圈。
Example 1:

Input:
[[1,1,0],
[1,1,0],
[0,0,1]]
Output: 2
Explanation:The 0th and 1st students are direct friends, so they are in a friend circle.
The 2nd student himself is in a friend circle. So return 2.

Example 2:

Input:
[[1,1,0],
[1,1,1],
[0,1,1]]
Output: 1
Explanation:The 0th and 1st students are direct friends, the 1st and 2nd students are direct friends,
so the 0th and 2nd students are indirect friends. All of them are in the same friend circle, so return 1.

解题思路:深度优先搜索

对遍历每个人,如果没有访问过,以此人为节点进行深度优先搜索,将所有的直接,间接朋友找出,并做标记,表示已经访问过。

class Solution {
public:
    int findCircleNum(vector<vector<int>>& M) {
        n = M.size();
        vector<int>people(n, 0);
        int res = 0;
        for (int i = 0; i < n; i++){
            if (people[i] == 0){
                res++;
                DFS(M, people, i);
            }
        }
        return res;
    }
    void DFS(vector<vector<int>>&M, vector<int>&people, int x){
        people[x] = 1;
        for (int i = 0; i < n; i++){
            if (M[i][x] == 1 && people[i] == 0)
                DFS(M, people, i);
        }
    }
    int n = 0;
};

2.4 填充封闭的区域

130. Surrounded Regions(Medium)
矩阵中只有字符 ‘X’ 和 ‘O’(大写字母O),将所有被 ‘X’ 包围的 ‘O’ 变成 ‘X’,边界不作为封闭条件。

Example:

X X X X
X O O X
X X O X
X O X X
After running your function, the board should be:
X X X X
X X X X
X X X X
X O X X

解题思路:

  1. 从四条边开始搜索 ‘O’,若边上存在 ‘O’,则从它开始进行 DFS 进行特殊标记,这部分的 ‘O’ 都不用变成 ‘X’;
  2. 标记完成后,则只剩下内部的 ‘O’ 了,此时直接将 ‘O’ 变成 ‘X’ 即可;
  3. 再将第一步的特殊标记变回 ‘O’ 即完成。
class Solution {
public:
    void solve(vector<vector<char>>& board) {
        if(board.empty() || board[0].empty())
            return;
        row = board.size();
        col = board[0].size();
        for (int i = 0; i < row; i++){
            dfs(board, i, 0);
            dfs(board, i, col-1);
        }
        for (int i = 1; i < col - 1; i++){
            dfs(board, 0, i);
            dfs(board, row-1, i);
        }
        for (int i = 0; i < row; i++){
            for (int j = 0; j < col; j++){
                if(board[i][j] == 'O')
                    board[i][j] = 'X';
                else if(board[i][j] == 'T')
                    board[i][j] = 'O';
            }
        }
    }
    void dfs(vector<vector<char>>& board, int i, int j) {
        if(i < 0 || i >= row || j < 0 || j >= col || board[i][j] != 'O')
            return;
        board[i][j] = 'T';
        dfs(board, i+1, j);
        dfs(board, i-1, j);
        dfs(board, i, j+1);
        dfs(board, i, j-1);
    }
private:
    int row;
    int col;
};

2.5 能到达的太平洋和大西洋的区域

417. Pacific Atlantic Water Flow(Medium)
上面和左侧是太平洋,下面和右侧是大西洋,矩阵中的每个值表示此处的海拔,水只能流向和原位置等高或低海拔的地方,找出所有可以流入太平洋和大西洋区域。

Given the following 5x5 matrix:

  Pacific ~   ~   ~   ~   ~ 
       ~  1   2   2   3  (5) *
       ~  3   2   3  (4) (4) *
       ~  2   4  (5)  3   1  *
       ~ (6) (7)  1   4   5  *
       ~ (5)  1   1   2   4  *
          *   *   *   *   * Atlantic

Return:
[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix).

解题思路
从四条边出发,深度优先搜索,将所有能访问到的节点标记,两个标记矩阵,一个表示可以从流入太平洋的区域,另一个表示可以流入大西洋的区域,找出二者共同的区域即可。

class Solution {
public:
    vector<vector<int>> pacificAtlantic(vector<vector<int>>& matrix) {
        vector<vector<int>>res;
        if(matrix.empty() || matrix[0].empty())
            return res;
        row = matrix.size();
        col = matrix[0].size();
        vector<vector<bool>>reachPacific(row, vector<bool>(col, 0));//可以流入太平洋的区域
        vector<vector<bool>>reachAtlantic(row, vector<bool>(col, 0));//可以流入大西洋的区域
        for (int i = 0; i < row; i++){
            DFS(matrix, reachPacific, i, 0);		//左侧边
            DFS(matrix, reachAtlantic, i, col-1);	//右侧边
        }
        for (int i = 0; i < col; i++){
            DFS(matrix, reachPacific, 0, i);		//上边
            DFS(matrix, reachAtlantic, row-1, i);	//下边
        }
        for(int i = 0; i < row; i++){
            for(int j = 0; j < col; j++)
                if(reachPacific[i][j] && reachAtlantic[i][j])//找都能到达的区域
                    res.push_back(vector<int>{i, j});
        }
        return res;
    }
    void DFS(vector<vector<int>>& matrix, vector<vector<bool>>& reach,int i, int j){
        reach[i][j] = 1;
        for(int a = 0; a < 4; a++){
            int nexti = i + dir[a][0];
            int nextj = j + dir[a][1];
            //下一个位置,在矩阵中,没有被访问,且大于等于当前海拔
            if(isValid(nexti,nextj) && !reach[nexti][nextj] && matrix[nexti][nextj] >= matrix[i][j])
                DFS(matrix, reach, nexti, nextj);
        }
    }
    //判断是否可以到达
    bool isValid(int i, int j){
	    return i >= 0 && i < row && j >= 0 && j < col;
    }
private:
    int row;
    int col;
    int dir[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值