Leetcode_入门_BFS和DFS

BFS

1、 二进制矩阵中的最短路径(1091、Medium)

1)题目要求

在这里插入图片描述

在这里插入图片描述

提示:

1 <= grid.length == grid[0].length <= 100
grid[i][j] 为 0 或 1

2)我的解法

DFS(超出内存)

class Solution {
    public Integer getResult(int i,int j,int[][] grid){
        Integer d=Integer.MAX_VALUE,r=Integer.MAX_VALUE,dr=Integer.MAX_VALUE;
        if(i==grid.length-1&&j==grid[0].length-1){
            if(grid[i][j]==0)return 1;
            else return null;
        }
        if(i<grid.length-1&&j<grid[0].length-1&&grid[i+1][j+1]==0
        &&getResult(i+1,j+1,grid)!=null)dr=getResult(i+1,j+1,grid);
        else if(i<grid.length&&j<grid[0].length-1&&grid[i][j+1]==0
        &&getResult(i,j+1,grid)!=null)r=getResult(i,j+1,grid);
        else if(i<grid.length-1&&j<grid[0].length&&grid[i+1][j]==0
        &&getResult(i+1,j,grid)!=null)d=getResult(i+1,j,grid);

        else if(i<grid.length&&j<grid[0].length&&j>0&&grid[i][j-1]==0
        &&getResult(i,j-1,grid)!=null)d=getResult(i,j-1,grid);
        Integer result=Math.min(dr,Math.min(d,r));
        return (result==Integer.MAX_VALUE)?null:result+1;


    }
    public int shortestPathBinaryMatrix(int[][] grid) {
        Integer result=getResult(0,0,grid);
        return (result==null)?-1:result;
    }
}

3)其他解法

1、模板:

void BFS()
{
    定义队列;
    定义备忘录,用于记录已经访问的位置;

    判断边界条件,是否能直接返回结果的。

    将起始位置加入到队列中,同时更新备忘录。

    while (队列不为空) {
        获取当前队列中的元素个数。
        for (元素个数) {
            取出一个位置节点。
            判断是否到达终点位置。
            获取它对应的下一个所有的节点。
            条件判断,过滤掉不符合条件的位置。
            新位置重新加入队列。
        }
    }

}

作者:hank-36
链接:link
来源:力扣(LeetCode)

2、

在这里插入图片描述

public class shortestPathBinaryMatrix {
    private static int[][] directions = {{0,1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {-1, -1}, {-1, 0}, {-1, 1}};
    private int row, col;
    public int shortestPathBinaryMatrix(int[][] grid) {
        row = grid.length;
        col = grid[0].length;
        if(grid[0][0] == 1 || grid[row - 1][col - 1] == 1) return -1;
        Queue<int[]> pos = new LinkedList<>();
        grid[0][0] = 1; // 直接用grid[i][j]记录从起点到这个点的最短路径长。按照题意 起点也有长度1
        pos.add(new int[]{0,0});
        while(!pos.isEmpty() && grid[row - 1][col - 1] == 0){ // 求最短路径 使用BFS
            int[] xy = pos.remove();
            int preLength = grid[xy[0]][xy[1]]; // 当前点的路径长度
            for(int i = 0; i < 8; i++){
                int newX = xy[0] + directions[i][0];
                int newY = xy[1] + directions[i][1];
                if(inGrid(newX, newY) && grid[newX][newY] == 0){
                    pos.add(new int[]{newX, newY});
                    grid[newX][newY] = preLength + 1; // 下一个点的路径长度要+1
                }
            }
        }
        return grid[row - 1][col - 1] == 0 ? -1 : grid[row - 1][col - 1]; // 如果最后终点的值还是0,说明没有到达
    }

    private boolean inGrid(int x, int y){
        return x >= 0 && x < row && y >= 0 && y < col;
    }
}


作者:ustcyyw
链接:link
来源:力扣(LeetCode)

3、

其实可以把这种层序遍历看作是水波,一圈圈往外扩散,最先扩散到边际的一定是最优解。
每传播一圈计数一次。

在这里插入图片描述

class Solution {
public:
    int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
        int n = grid.size() - 1;
        if(n == 0 && grid[0][0] == 0)
            return 1;
        if(grid[0][0] == 1 || grid[n][n] == 1)
            return -1;
        queue<pair<int, int>> que;
        que.push({0,0});
        int count = 0;
        vector<vector<int>> lists = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};
        while(!que.empty()){
            int LevelNo = que.size();
            count++;    ********//传播一圈,计数一次。********
            while(LevelNo-- > 0){
                pair<int, int> Tep = que.front();
                grid[Tep.first][Tep.second] = 1;
                que.pop();
                for(int i = 0; i < 8; i++){
                    int x = Tep.first - lists[i][0];
                    int y = Tep.second - lists[i][1];
                    if(x == n && y == n)
                        return count + 1;
                    if(x < 0 || x > n || y < 0 || y > n || grid[x][y] == 1)
                        continue;
                    else if(grid[x][y] == 0)
                        que.push({x, y});   
                    grid[x][y] = 1; 
                }
            }

        }
        return -1;
    }
};


作者:niu-niu-niu-c
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

class Solution {
    public int shortestPathBinaryMatrix(int[][] grid) {
        //grid可以充当is_visit,等于0即未访问过且可以走,
        //1即访问过,或者阻塞,总之不可以走
        Queue<int[]> q=new LinkedList<>();
        if(grid[0][0]==1||grid[grid.length-1][grid[0].length-1]==1)return -1;
        grid[0][0]=1;
        int count=0;
        q.offer(new int[]{0,0});
        while(!q.isEmpty()){
            int size=q.size();
            count++;//每层+1
            while(size>0){
                int[] pair=q.poll();
                int i=pair[0],j=pair[1];
                grid[i][j]=1;
                if(i==grid.length-1&&j==grid[0].length-1)return count;
                //最先到终点的一定是最短
            //将八个方向中是0的加进去
                if(manzu(i+1,j,grid)&&grid[i+1][j]==0)
                {q.offer(new int[]{i+1,j});grid[i+1][j]=grid[i][j]+1;}
                if(manzu(i,j+1,grid)&&grid[i][j+1]==0)
                {q.offer(new int[]{i,j+1});grid[i][j+1]=grid[i][j]+1;}
                if(manzu(i-1,j,grid)&&grid[i-1][j]==0)
                {q.offer(new int[]{i-1,j});grid[i-1][j]=grid[i][j]+1;}
                if(manzu(i,j-1,grid)&&grid[i][j-1]==0)
                {q.offer(new int[]{i,j-1});grid[i][j-1]=grid[i][j]+1;}
                if(manzu(i+1,j+1,grid)&&grid[i+1][j+1]==0)
                {q.offer(new int[]{i+1,j+1});grid[i+1][j+1]=grid[i][j]+1;}
                if(manzu(i+1,j-1,grid)&&grid[i+1][j-1]==0)
                {q.offer(new int[]{i+1,j-1});grid[i+1][j-1]=grid[i][j]+1;}
                if(manzu(i-1,j+1,grid)&&grid[i-1][j+1]==0)
                {q.offer(new int[]{i-1,j+1});grid[i-1][j+1]=grid[i][j]+1;}
                if(manzu(i-1,j-1,grid)&&grid[i-1][j-1]==0)
                {q.offer(new int[]{i-1,j-1});grid[i-1][j-1]=grid[i][j]+1;}
                size--;
            }
        }
        return -1;
    }
    public boolean manzu(int i,int j,int[][] grid){//判断是否越界
        return (i>=0&&i<grid.length&&j>=0&&j<grid[0].length);
    }
}

5)学到的东西

java 队列
用LinkedList实现
API:
isEmpty() 不是empty()

在这里插入图片描述

BFS基本思想

在这里插入图片描述

广度优先搜索一层一层地进行遍历,每层遍历都是以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。

第一层:

0 -> {6,2,1,5}
第二层:

6 -> {4}
2 -> {}
1 -> {}
5 -> {3}
第三层:

4 -> {}
3 -> {}
每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 最优解 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径,无权图是指从一个节点到另一个节点的代价都记为 1。

在程序实现 BFS 时需要考虑以下问题:

队列:用来存储每一轮遍历得到的节点;
标记:对于遍历过的节点,应该将它标记,防止重复遍历。

模板:

void BFS()
{
    定义队列;
    定义备忘录,用于记录已经访问的位置;

    判断边界条件,是否能直接返回结果的。

    将起始位置加入到队列中,同时更新备忘录。

    while (队列不为空) {
        获取当前队列中的元素个数。
        for (元素个数) {
            取出一个位置节点。
            判断是否到达终点位置。
            获取它对应的下一个所有的节点。
            条件判断,过滤掉不符合条件的位置。
            新位置重新加入队列。
        }
    }

}

此题中grid可以充当备忘录

2、 单词接龙(127、Medium)

1)题目要求

给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度。转换需遵循如下规则:

每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。
说明:

如果不存在这样的转换序列,返回 0。
所有单词具有相同的长度。
所有单词只由小写字母组成。
字典中不存在重复的单词。
你可以假设 beginWord 和 endWord 是非空的,且二者不相同。
示例 1:

输入:
beginWord = “hit”,
endWord = “cog”,
wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]

输出: 5

解释: 一个最短转换序列是 “hit” -> “hot” -> “dot” -> “dog” -> “cog”,
返回它的长度 5。
示例 2:

输入:
beginWord = “hit”
endWord = “cog”
wordList = [“hot”,“dot”,“dog”,“lot”,“log”]

输出: 0

解释: endWord “cog” 不在字典中,所以无法进行转换。

2)我的解法


class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        Queue<String> q=new LinkedList<>();
        q.offer(beginWord);
        int count=0;
        while(!q.isEmpty()){
            count++;
            int size=q.size();
            while(size-- >0){
                 String s=q.poll();
                 if(s.equals(endWord))return count;
                 for(int i=0;i<wordList.size();i++){
                     if(isNext(s,wordList.get(i))){
                         q.offer(wordList.get(i));
                         wordList.remove(i);
                         i--;
                     }
                 }
            }

        }
        return 0;
    }
    boolean isNext(String a,String b){
        boolean isTwice=false;
        for(int i=0;i<a.length();i++){
            if(a.charAt(i)!=b.charAt(i)){
                if(isTwice)return false;
                else isTwice=true;
            }
        }
        return true;
    }
}

3)其他解法

在这里插入图片描述

广度优先

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

public class Solution {

    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        // 第 1 步:先将 wordList 放到哈希表里,便于判断某个单词是否在 wordList 里
        Set<String> wordSet = new HashSet<>(wordList);
        if (wordSet.size() == 0 || !wordSet.contains(endWord)) {
            return 0;
        }
        wordSet.remove(beginWord);
        
        // 第 2 步:图的广度优先遍历,必须使用队列和表示是否访问过的 visited 哈希表
        Queue<String> queue = new LinkedList<>();
        queue.offer(beginWord);
        Set<String> visited = new HashSet<>();
        visited.add(beginWord);
        
        // 第 3 步:开始广度优先遍历,包含起点,因此初始化的时候步数为 1
        int step = 1;
        while (!queue.isEmpty()) {
            int currentSize = queue.size();
            for (int i = 0; i < currentSize; i++) {
                // 依次遍历当前队列中的单词
                String currentWord = queue.poll();
                // 如果 currentWord 能够修改 1 个字符与 endWord 相同,则返回 step + 1
                if (changeWordEveryOneLetter(currentWord, endWord, queue, visited, wordSet)) {
                    return step + 1;
                }
            }
            step++;
        }
        return 0;
    }

    /**
     * 尝试对 currentWord 修改每一个字符,看看是不是能与 endWord 匹配
     *
     * @param currentWord
     * @param endWord
     * @param queue
     * @param visited
     * @param wordSet
     * @return
     */
    private boolean changeWordEveryOneLetter(String currentWord, String endWord,
                                             Queue<String> queue, Set<String> visited, Set<String> wordSet) {
        char[] charArray = currentWord.toCharArray();
        for (int i = 0; i < endWord.length(); i++) {
            // 先保存,然后恢复
            char originChar = charArray[i];
            for (char k = 'a'; k <= 'z'; k++) {
                if (k == originChar) {
                    continue;
                }
                charArray[i] = k;
                String nextWord = String.valueOf(charArray);
                if (wordSet.contains(nextWord)) {
                    if (nextWord.equals(endWord)) {
                        return true;
                    }
                    if (!visited.contains(nextWord)) {
                        queue.add(nextWord);
                        // 注意:添加到队列以后,必须马上标记为已经访问
                        visited.add(nextWord);
                    }
                }
            }
            // 恢复
            charArray[i] = originChar;
        }
        return false;
    }
}

4)自己的优化代码

class Solution {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        Queue<String> q=new LinkedList<>();
        q.offer(beginWord);
        int count=0;
        while(!q.isEmpty()){
            count++;
            int size=q.size();
            while(size-- >0){
                 String s=q.poll();
                 if(s.equals(endWord))return count;
                 for(int i=0;i<wordList.size();i++){
                     if(isNext(s,wordList.get(i))){
                         q.offer(wordList.get(i));
                         wordList.remove(i);
                         i--;
                     }
                 }
            }

        }
        return 0;
    }
    boolean isNext(String a,String b){
        boolean isTwice=false;
        for(int i=0;i<a.length();i++){
            if(a.charAt(i)!=b.charAt(i)){
                if(isTwice)return false;
                else isTwice=true;
            }
        }
        return true;
    }
}

5)学到的东西

最短路径基本都可以用BFS

DFS

1、 岛屿的最大面积(695、Medium)

1)题目要求

给定一个包含了一些 0 和 1 的非空二维数组 grid 。

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)

示例 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]]
对于上面这个给定矩阵应返回 6。注意答案不应该是 11 ,因为岛屿只能包含水平或垂直的四个方向的 1 。

示例 2:

[[0,0,0,0,0,0,0,0]]
对于上面这个给定的矩阵, 返回 0。

2)我的解法

class Solution {
    int col=0,row=0;
    int max=0;
    public int dfs(int i,int j,int[][] grid){
        int l=0,r=0,u=0,d=0;
        grid[i][j]=-1;
        if(isLegal(i-1,j)&&grid[i-1][j]==1)u=dfs(i-1,j,grid);

        if(isLegal(i+1,j)&&grid[i+1][j]==1)d=dfs(i+1,j,grid);

        if(isLegal(i,j-1)&&grid[i][j-1]==1)l=dfs(i,j-1,grid);

        if(isLegal(i,j+1)&&grid[i][j+1]==1)r=dfs(i,j+1,grid);

        return 1+u+d+l+r;
    }
    public int maxAreaOfIsland(int[][] grid) {
        col=grid.length;
        row=grid[0].length;
        for(int i=0;i<col;i++){
            for(int j=0;j<row;j++){
                if(grid[i][j]==1)max=Math.max(max,dfs(i,j,grid));
            }
        }
        return max;
    }
    public boolean isLegal(int i,int j){
        return (i>=0&&i<col&&j>=0&&j<row);
    }
}

3)其他解法

1

class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int res = 0; 
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length; j++) {
                if (grid[i][j] == 1) {
                    res = Math.max(res, dfs(i, j, grid));
                }
            }
        } 
        return res;
    }
    // 每次调用的时候默认num为1,进入后判断如果不是岛屿,则直接返回0,就可以避免预防错误的情况。
    // 每次找到岛屿,则直接把找到的岛屿改成0,这是传说中的沉岛思想,就是遇到岛屿就把他和周围的全部沉默。
    // ps:如果能用沉岛思想,那么自然可以用朋友圈思想。有兴趣的朋友可以去尝试。
    private int dfs(int i, int j, int[][] grid) {
        if (i < 0 || j < 0 || i >= grid.length || j >= grid[i].length || grid[i][j] == 0) { 
            return 0;
        } 
        grid[i][j] = 0;
        int num = 1;
        num += dfs(i + 1, j, grid);
        num += dfs(i - 1, j, grid);
        num += dfs(i, j + 1, grid);
        num += dfs(i, j - 1, grid);
        return num;
        
    }
}

作者:mark-42
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

class Solution {
    int col=0,row=0;
    public int dfs(int i,int j,int[][] grid){
        int l=0,r=0,u=0,d=0;
        grid[i][j]=-1;
        if(isLegal(i-1,j)&&grid[i-1][j]==1)u=dfs(i-1,j,grid);

        if(isLegal(i+1,j)&&grid[i+1][j]==1)d=dfs(i+1,j,grid);

        if(isLegal(i,j-1)&&grid[i][j-1]==1)l=dfs(i,j-1,grid);

        if(isLegal(i,j+1)&&grid[i][j+1]==1)r=dfs(i,j+1,grid);

        return 1+u+d+l+r;
    }
    public int maxAreaOfIsland(int[][] grid) {
        col=grid.length;
        row=grid[0].length;
        int max=0;
        for(int i=0;i<col;i++){
            for(int j=0;j<row;j++){
                if(grid[i][j]==1)max=Math.max(max,dfs(i,j,grid));
            }
        }
        return max;
    }
    public boolean isLegal(int i,int j){
        return (i>=0&&i<col&&j>=0&&j<row);
    }
}

5)学到的东西

dfs没必要全局用,可以局部用dfs,比如本题仅当遇到1时用一下

在这里插入图片描述

本题中把grid 用来标记,访问过的改为-1

2、

1)题目要求

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:
[
[‘1’,‘1’,‘1’,‘1’,‘0’],
[‘1’,‘1’,‘0’,‘1’,‘0’],
[‘1’,‘1’,‘0’,‘0’,‘0’],
[‘0’,‘0’,‘0’,‘0’,‘0’]
]
输出: 1
示例 2:

输入:
[
[‘1’,‘1’,‘0’,‘0’,‘0’],
[‘1’,‘1’,‘0’,‘0’,‘0’],
[‘0’,‘0’,‘1’,‘0’,‘0’],
[‘0’,‘0’,‘0’,‘1’,‘1’]
]
输出: 3
解释: 每座岛屿只能由水平和/或竖直方向上相邻的陆地连接而成。

2)我的解法

class Solution {
    int col=0,row=0;
    public void dfs(int i,int j,char[][] grid){
        grid[i][j]='a';
        if(isLegal(i-1,j)&&grid[i-1][j]=='1')dfs(i-1,j,grid);

        if(isLegal(i+1,j)&&grid[i+1][j]=='1')dfs(i+1,j,grid);

        if(isLegal(i,j-1)&&grid[i][j-1]=='1')dfs(i,j-1,grid);

        if(isLegal(i,j+1)&&grid[i][j+1]=='1')dfs(i,j+1,grid);
    }
    public int numIslands(char[][] grid) {
        int num=0;
        col=grid.length;
        if(col==0)return 0;
        row=grid[0].length;
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j]=='1'){
                    dfs(i,j,grid);
                    num++;
                }
            }
        }
        return num;
    }
    public boolean isLegal(int i,int j){
        return (i>=0&&i<col&&j>=0&&j<row);
    }
}

3)其他解法

DFS

class Solution {
    void dfs(char[][] grid, int r, int c) {
        int nr = grid.length;
        int nc = grid[0].length;

        if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') {
            return;
        }

        grid[r][c] = '0';
        dfs(grid, r - 1, c);
        dfs(grid, r + 1, c);
        dfs(grid, r, c - 1);
        dfs(grid, r, c + 1);
    }

    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int nr = grid.length;
        int nc = grid[0].length;
        int num_islands = 0;
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    ++num_islands;
                    dfs(grid, r, c);
                }
            }
        }

        return num_islands;
    }
}

BFS

class Solution {
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int nr = grid.length;
        int nc = grid[0].length;
        int num_islands = 0;

        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    ++num_islands;
                    grid[r][c] = '0';
                    Queue<Integer> neighbors = new LinkedList<>();
                    neighbors.add(r * nc + c);
                    while (!neighbors.isEmpty()) {
                        int id = neighbors.remove();
                        int row = id / nc;
                        int col = id % nc;
                        if (row - 1 >= 0 && grid[row-1][col] == '1') {
                            neighbors.add((row-1) * nc + col);
                            grid[row-1][col] = '0';
                        }
                        if (row + 1 < nr && grid[row+1][col] == '1') {
                            neighbors.add((row+1) * nc + col);
                            grid[row+1][col] = '0';
                        }
                        if (col - 1 >= 0 && grid[row][col-1] == '1') {
                            neighbors.add(row * nc + col-1);
                            grid[row][col-1] = '0';
                        }
                        if (col + 1 < nc && grid[row][col+1] == '1') {
                            neighbors.add(row * nc + col+1);
                            grid[row][col+1] = '0';
                        }
                    }
                }
            }
        }

        return num_islands;
    }
}

作者:LeetCode
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

class Solution {
    int col=0,row=0;
    public void dfs(int i,int j,char[][] grid){
        if(!(i>=0&&i<col&&j>=0&&j<row&&grid[i][j]=='1'))return;
        grid[i][j]='a';
        dfs(i-1,j,grid);

        dfs(i+1,j,grid);

        dfs(i,j-1,grid);

        dfs(i,j+1,grid);
    }
    public int numIslands(char[][] grid) {
        int num=0;
        col=grid.length;
        if(col==0)return 0;
        row=grid[0].length;
        for(int i=0;i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j]=='1'){
                    dfs(i,j,grid);
                    num++;
                }
            }
        }
        return num;
    }
}

5)学到的东西

DFS

BFS

3、朋友圈(547、Medium)

1)题目要求

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

示例 1:

输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出:2
解释:已知学生 0 和学生 1 互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回 2 。
示例 2:

输入:
[[1,1,0],
[1,1,1],
[0,1,1]]
输出:1
解释:已知学生 0 和学生 1 互为朋友,学生 1 和学生 2 互为朋友,所以学生 0 和学生 2 也是朋友,所以他们三个在一个朋友圈,返回 1 。

提示:

1 <= N <= 200
M[i][i] == 1
M[i][j] == M[j][i]

2)我的解法

class Solution {
    public void dfs(int i,int[][] grid){
        //遍历这一行
        for(int k=0;k<grid[0].length;k++){
            //如果有关系,去遍历那一行
            if(grid[i][k]==1){
                grid[i][k]=-1;
                grid[k][i]=-1;
                if(i!=k)dfs(k,grid);
            }
        }

    }
    public int findCircleNum(int[][] M) {
        int count=0;
        for(int i=0;i<M.length;i++){
            if(M[i][i]==1){
                count++;//如果等于1,说明之前DFS时未遍历到此行
                dfs(i,M);
            }
            //不等于1说明遍历过此行了
        }
        return count;
    }
}

3)其他解法

DFS


public class Solution {
    public void dfs(int[][] M, int[] visited, int i) {
        for (int j = 0; j < M.length; j++) {
            if (M[i][j] == 1 && visited[j] == 0) {
                visited[j] = 1;
                dfs(M, visited, j);
            }
        }
    }
    public int findCircleNum(int[][] M) {
        int[] visited = new int[M.length];
        int count = 0;
        for (int i = 0; i < M.length; i++) {
            if (visited[i] == 0) {
                dfs(M, visited, i);
                count++;
            }
        }
        return count;
    }
}


BFS

public class Solution {
    public int findCircleNum(int[][] M) {
        int[] visited = new int[M.length];
        int count = 0;
        Queue < Integer > queue = new LinkedList < > ();
        for (int i = 0; i < M.length; i++) {
            if (visited[i] == 0) {
                queue.add(i);
                while (!queue.isEmpty()) {
                    int s = queue.remove();
                    visited[s] = 1;
                    for (int j = 0; j < M.length; j++) {
                        if (M[s][j] == 1 && visited[j] == 0)
                            queue.add(j);
                    }
                }
                count++;
            }
        }
        return count;
    }
}


4)自己的优化代码

class Solution {
    public void dfs(int i,int[][] grid){
        //遍历这一行
        for(int k=0;k<grid[0].length;k++){
            //如果有关系,去遍历那一行
            if(grid[i][k]==1){
                grid[i][k]=-1;
                grid[k][i]=-1;
                if(i!=k)dfs(k,grid);
            }
        }

    }
    public int findCircleNum(int[][] M) {
        int count=0;
        for(int i=0;i<M.length;i++){
            if(M[i][i]==1){
                count++;//如果等于1,说明之前DFS时未遍历到此行
                dfs(i,M);
            }
            //不等于1说明遍历过此行了
        }
        return count;
    }
}

5)学到的东西

DFS,DFS函数中不一定每次只遍历一个元素,如本题在DFS函数中遍历了一行

BFS

4、被围绕的区域(130、Medium)

1)题目要求

给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。

找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

示例:

X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:

X X X X
X X X X
X X X X
X O X X
解释:

被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

2)我的解法

class Solution {
    List<int[]> temp=new ArrayList<>();
    public boolean is_dfs(int i,int j,char[][] board){
        if(board[i][j]=='X')return true;
        if(i==0||j==0||i==board.length-1||j==board[0].length-1)return false;
        board[i][j]='X';
        temp.add(new int[]{i,j});
        return(is_dfs(i-1,j,board)&&is_dfs(i+1,j,board)&&is_dfs(i,j-1,board)&&is_dfs(i,j+1,board));
    }
    public void solve(char[][] board) {
        for(int i=1;i<board.length-1;i++){
            for(int j=1;j<board[0].length-1;j++){
                if(board[i][j]=='O'){
                    if(!is_dfs(i,j,board)){//如果能连接到边上的O,进行反悔操作
                        for(int[] pair:temp){
                            board[pair[0]][pair[1]]='O';
                        }
                    }
                    temp.clear();
                }
            }
        }
        return;
    }
}

3)其他解法

DFS

在这里插入图片描述

class Solution {
    int n, m;

    public void solve(char[][] board) {
        n = board.length;
        if (n == 0) {
            return;
        }
        m = board[0].length;
        for (int i = 0; i < n; i++) {
            dfs(board, i, 0);
            dfs(board, i, m - 1);
        }
        for (int i = 1; i < m - 1; 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] == 'A') {
                    board[i][j] = 'O';
                } else if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                }
            }
        }
    }

    public void dfs(char[][] board, int x, int y) {
        if (x < 0 || x >= n || y < 0 || y >= m || 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);
    }
}

BFS

class Solution {
    int[] dx = {1, -1, 0, 0};
    int[] dy = {0, 0, 1, -1};

    public void solve(char[][] board) {
        int n = board.length;
        if (n == 0) {
            return;
        }
        int m = board[0].length;
        Queue<int[]> queue = new LinkedList<int[]>();
        for (int i = 0; i < n; i++) {
            if (board[i][0] == 'O') {
                queue.offer(new int[]{i, 0});
            }
            if (board[i][m - 1] == 'O') {
                queue.offer(new int[]{i, m - 1});
            }
        }
        for (int i = 1; i < m - 1; i++) {
            if (board[0][i] == 'O') {
                queue.offer(new int[]{0, i});
            }
            if (board[n - 1][i] == 'O') {
                queue.offer(new int[]{n - 1, i});
            }
        }
        while (!queue.isEmpty()) {
            int[] cell = queue.poll();
            int x = cell[0], y = cell[1];
            board[x][y] = 'A';
            for (int i = 0; i < 4; i++) {
                int mx = x + dx[i], my = y + dy[i];
                if (mx < 0 || my < 0 || mx >= n || my >= m || board[mx][my] != 'O') {
                    continue;
                }
                queue.offer(new int[]{mx, my});
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (board[i][j] == 'A') {
                    board[i][j] = 'O';
                } else if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                }
            }
        }
    }
}

作者:LeetCode-Solution
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

class Solution {
    public void dfs(int i,int j,char[][] board){
        if(!(i>=0&&i<board.length&&j>=0&&j<board[0].length))return;
        if(board[i][j]=='X'||board[i][j]=='A')return ;
        board[i][j]='A';
        dfs(i-1,j,board);
        dfs(i+1,j,board);
        dfs(i,j-1,board);
        dfs(i,j+1,board);
    }
    public void solve(char[][] board) {
        if(board.length==0)return ;
        for(int i=0;i<board[0].length;i++){
            if(board[0][i]=='O')dfs(0,i,board);
            if(board[board.length-1][i]=='O')dfs(board.length-1,i,board);
        }
        for(int i=1;i<board.length-1;i++){
            if(board[i][0]=='O')dfs(i,0,board);
            if(board[i][board[0].length-1]=='O')dfs(i,board[0].length-1,board);
        }
        for(int i=0;i<board.length;i++){
            for(int j=0;j<board[0].length;j++){
                if(board[i][j]=='A'){
                    board[i][j]='O';
                }
                else board[i][j]='X';
            }
        }
        return;
    }
}

5)学到的东西

DFS
先遍历四周,之后再全部重新遍历一遍,遍历过’O’的恢复为’O‘,其他’O’改为’X’
这样可以避免反悔操作

做题时尽量避免反悔操作

5、太平洋大西洋水流问题(417、Medium)

1)题目要求

给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。

规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。

请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。

提示:

输出坐标的顺序不重要
m 和 n 都小于150

示例:

给定下面的 5x5 矩阵:

太平洋 ~ ~ ~ ~ ~
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 *
          • 大西洋

返回:

[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (上图中带括号的单元).

2)我的解法

没想出来

3)其他解法

在这里插入图片描述


public class pacificAtlantic {
    private static int[][] dires = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    private int m, n;
    private int[][] matrix;

    public List<List<Integer>> pacificAtlantic(int[][] matrix) {
        List<List<Integer>> res = new ArrayList<>();
        m = matrix.length;
        if (m == 0)
            return res;
        n = matrix[0].length;
        if (n == 0)
            return res;
        this.matrix = matrix;
        boolean[][] canReachP = new boolean[m][n];
        boolean[][] canReachA = new boolean[m][n];
        for (int i = 0; i < n; i++) {
            dfs(0, i, canReachP);
            dfs(m - 1, i, canReachA);
        }
        for (int i = 0; i < m; i++) {
            dfs(i, 0, canReachP);
            dfs(i, n - 1, canReachA);
        }
        //找出交集
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(canReachA[i][j] && canReachP[i][j]){
                    List<Integer> temp = new ArrayList<>();
                    temp.add(i);
                    temp.add(j);
                    res.add(temp);
                }
            }
        }
        return res;
    }
    /**
     * 换一种思路,从边界往里面走,只能走到比自己更高或者等高的地方。边界能走到的地方,就是能流入对应海洋的地方。
     */
    private void dfs(int x, int y, boolean[][] canReach) {
        canReach[x][y] = true;
        for (int i = 0; i < 4; i++) {
            int newX = x + dires[i][0];
            int newY = y + dires[i][1];
            if (isIn(newX, newY) && matrix[x][y] <= matrix[newX][newY] && !canReach[newX][newY]) {
                dfs(newX, newY, canReach);
            }
        }
    }

    private boolean isIn(int x, int y) {
        return x >= 0 && x < m && y >= 0 && y < n;
    }
}

作者:ustcyyw
链接:link
来源:力扣(LeetCode)

4)自己的优化代码

class Solution {
    Set<List<Integer>> set1;
    Set<List<Integer>> set2;
    List<List<Integer>> result;
    public void dfs1(int i,int j,int[][] matrix){//即太平洋
      //交集放入result
       if(set2.contains(Arrays.asList(i,j)))result.add(Arrays.asList(i,j));
        set1.add(Arrays.asList(i,j));
	
	//遍历>=自己的结点,并全部记录,set可充当备忘录(visited)
        if(isLegal(i-1,j,matrix)&&!set1.contains(Arrays.asList(i-1,j))&&matrix[i][j]<=matrix[i-1][j])dfs1(i-1,j,matrix);
        if(isLegal(i+1,j,matrix)&&!set1.contains(Arrays.asList(i+1,j))&&matrix[i][j]<=matrix[i+1][j])dfs1(i+1,j,matrix);
        if(isLegal(i,j-1,matrix)&&!set1.contains(Arrays.asList(i,j-1))&&matrix[i][j]<=matrix[i][j-1])dfs1(i,j-1,matrix);
        if(isLegal(i,j+1,matrix)&&!set1.contains(Arrays.asList(i,j+1))&&matrix[i][j]<=matrix[i][j+1])dfs1(i,j+1,matrix);
    }
    public void dfs2(int i,int j,int[][] matrix){//即大西洋
        if(set1.contains(Arrays.asList(i,j)))result.add(Arrays.asList(i,j));
        set2.add(Arrays.asList(i,j));
        if(isLegal(i-1,j,matrix)&&!set2.contains(Arrays.asList(i-1,j))&&matrix[i][j]<=matrix[i-1][j])dfs2(i-1,j,matrix);
        if(isLegal(i+1,j,matrix)&&!set2.contains(Arrays.asList(i+1,j))&&matrix[i][j]<=matrix[i+1][j])dfs2(i+1,j,matrix);
        if(isLegal(i,j-1,matrix)&&!set2.contains(Arrays.asList(i,j-1))&&matrix[i][j]<=matrix[i][j-1])dfs2(i,j-1,matrix);
        if(isLegal(i,j+1,matrix)&&!set2.contains(Arrays.asList(i,j+1))&&matrix[i][j]<=matrix[i][j+1])dfs2(i,j+1,matrix);
        return;
    }
    public boolean isLegal(int i,int j,int[][] matrix){
        return (i>=0&&j>=0&&i<matrix.length&&j<matrix[0].length);
    }
    public List<List<Integer>> pacificAtlantic(int[][] matrix) {
        result=new ArrayList<>();
        set1=new HashSet<>();
        set2=new HashSet<>();
        if(matrix.length==0)return result;
        for(int i=0;i<matrix[0].length;i++){
            if(!set1.contains(Arrays.asList(0,i)))dfs1(0,i,matrix);
            if(!set2.contains(Arrays.asList(matrix.length-1,i)))dfs2(matrix.length-1,i,matrix);
        }
        for(int i=0;i<matrix.length;i++){
            if(!set1.contains(Arrays.asList(i,0)))dfs1(i,0,matrix);
            if(!set2.contains(Arrays.asList(i,matrix[0].length-1)))dfs2(i,matrix[0].length-1,matrix);
        }
        return result;
    }
}

5)学到的东西

DFS:不要总想着遍历所有结点,比如本题,只需对所有边界结点进行DFS即可找出,然后找出交集即可

找交集可以用set,也可以弄两个备忘录,当两个备忘录中都为true即为交集

思想:遍历四周

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值