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即为交集
思想:遍历四周