力扣经典深度优先+递归算法

深度优先算法

在这里插入图片描述

1.有一个二维矩阵 grid ,每个位置要么是陆地(记号为 0 )要么是水域(记号为 1 )。
我们从一块陆地出发,每次可以往上下左右 4 个方向相邻区域走,能走到的所有陆地区域,我们将其称为一座「岛屿」。
如果一座岛屿 完全 由水域包围,即陆地边缘上下左右所有相邻区域都是水域,那么我们将其称为 「封闭岛屿」。
请返回封闭岛屿的数目。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-closed-islands
在这里插入图片描述
代码

class Solution {
    
    
    int[][] dir = new int[][]{
    
    //定义四个方向
        {
    
    1,0},{
    
    -1,0},{
    
    0,-1},{
    
    0,1}
    };
    boolean[][] visited;//用于判断是否走过
    public int closedIsland(int[][] grid) {
    
    
        int row = grid.length;//获取行数
        int col = grid[0].length;//获取列数
        visited=new boolean[row][col];//初始化数组
        int count=0;//定义封闭岛数
        for(int i=0;i<row;i++){
    
    //循环遍历每一个位子
            for(int j=0;j<col;j++){
    
    
                if(!visited[i][j]&&grid[i][j]==0){
    
    //如果当前位置没有走过,且当前位置是岛屿
                    if(dfsFindLand(grid,col,row,i,j))//递归判断当前是不是封闭岛屿
                    count++;//是的话++
                }
            }
        }
        return count;//最终返回结果
    }
    public boolean dfsFindLand(int[][] grid,int col,int row,int curRow,int curCol){
    
    //深度优先算法
       if(curCol>=col||curCol<0) return false;//越界处理
       if(curRow>=row||curRow<0)  return false;//越界处理
       if(grid[curRow][curCol]==1||visited[curRow][curCol]) return true;//当前位置是海,或者已经走过,返回true
       visited[curRow][curCol]=true;//设置当前位置已经访问过
       boolean flag = true;//定义一个标记
       for(int arr[]:dir){
    
    //循环走四个方向,
           if(!dfsFindLand(grid,col,row,curRow+arr[0],curCol+arr[1]))
                    flag =false;//当前位置不是一个闭环岛屿
       }
       return flag;
    }
}

2.给定一个包含了一些 0 和 1 的非空二维数组 grid 。
一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-area-of-island
在这里插入图片描述
代码:

class Solution {
    
    
    int row,col;//定义全局变量行和列
    int cnt ;//当前的岛屿面积
    public int maxAreaOfIsland(int[][] grid) {
    
    
        row = grid.length;//获取行
        col = grid[0].length;//获取列
        int res=0;//初始话,记录最大岛屿面积
        for(int i=0;i<row;i++){
    
    //循环遍历。。。
            for(int j=0;j<col;j++){
    
    
                if(grid[i][j]==1){
    
    
                    cnt=dfs(grid,i,j);//记录当前位置出发形成的岛屿数
                     if(cnt>res){
    
    //取最大值
                         res=cnt;
                     }
                }
            }
        }
        return res;
    }
    public int dfs(int[][] grid,int x,int y){
    
    //同样采用深度优先算法
        int count=1;//初始化
        if(x>=row||x<0||y>=col||y<0||grid[x][y]==0){
    
    //越界或当前是海洋
            return 0;
        }
        grid[x][y]=0;//表示当前位置已经计算了,将其设置为海
        count+=dfs(grid,x+1,y);//每个方向去访问,并记录岛屿数
        count+=dfs(grid,x-1,y);
       count+= dfs(grid,x,y-1);
        count+=dfs(grid,x,y+1);
        return count;//最后返回总数
    }
}

3.「以扣会友」线下活动所在场地由若干主题空间与走廊组成,场地的地图记作由一维字符串型数组 grid,字符串中仅包含 “0"~"5” 这 6 个字符。地图上每一个字符代表面积为 1 的区域,其中 “0” 表示走廊,其他字符表示主题空间。相同且连续(连续指上、下、左、右四个方向连接)的字符组成同一个主题空间。
假如整个 grid 区域的外侧均为走廊。请问,不与走廊直接相邻的主题空间的最大面积是多少?如果不存在这样的空间请返回 0。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/YesdPw
在这里插入图片描述
代码:

class Solution {
    
    
//声明全局变量
    boolean[][] vis;//设置是否访问
    boolean adjoin;//记录当前是否为走廊
    int row,col;//行列
    int cnt;//记录最大的主题空间
    public int largestArea(String[] grid) {
    
    
         row = grid.length;//行
         col = grid[0].length();//列
        vis = new boolean[row][col];//初始化
        int res=0;//记录最大数的主题空间
        for(int i=0;i<row;i++){
    
    //循环遍历每个角标
            for(int j=0;j<col;j++){
    
    
                if(!vis[i][j]){
    
    //没有访问过
                    adjoin=(grid[i].charAt(j)=='0');//当前是否为走廊
                    cnt=0;//初始值为0
                    dfs(grid,i,j,grid[i].charAt(j));//深度优先算法
                    if(!adjoin){
    
    //当前不是走廊
                        res = res>cnt?res:cnt;//去最大值
                    }
                }
            }
        }
    return res;//返回最大值
    }
    public void dfs(String[] grid,int x,int y,char ch){
    
    //深度优先算法,这里加入一个char ch的目的是在于主题空间不唯一,只有相同的主题空间才能被组合
        if(x<0||y<0||x>=row||y>=col){
    
    //越界处理
            adjoin=true;
            return;
        }
        char cur = grid[x].charAt(y);//获取当前位置的主题号
        if(cur!=ch||vis[x][y]){
    
    //如果当前位置走过,或者当前主题数不相等,都会退出
            if(cur=='0'){
    
    
                adjoin =true;
            }
            return;
        }
        vis[x][y] = true;//当前主题已经计算
        cnt++;//主题数加1
        dfs(grid,x+1,y,ch);//四个方向去寻找相同的主题,构成最大的主题空间
        dfs(grid,x,y+1,ch);
        dfs(grid,x-1,y,ch);
        dfs(grid,x,y-1,ch);
    }
}

4.迷宫
这道题是自己加的,在学习过程中碰到过类似的题目!

public class Maze {
    
    
private static LinkedList<String> stack = new LinkedList<>();//双向循环链表,既可以是栈结构也可以是队列
private static int enterX = 1;//起点
private static int enterY = 0;
private static int exitX = 7;//终点
private static int exitY = 8;
private static boolean[][] visited = new boolean[9][9];//同样用于记录是否访问过
private static int direction[][] = {
    
    {
    
    -1, 0}, {
    
    0, 1}, {
    
    1, 0}, {
    
    0, -1}};//四个方向
private static int[][] maze = {
    
    //迷宫二维数组,0表示路,1表示墙
{
    
    1, 1, 1, 1, 1, 1, 1, 1, 1},
{
    
    0, 0, 1, 0, 0, 0, 1, 1, 1},
{
    
    1, 0, 1, 1, 1, 0, 1, 1, 1},
{
    
    1, 0, 0, 1, 0, 0, 1, 1, 1},
{
    
    1, 1, 0, 1, 1, 0, 0, 0, 1},
{
    
    1, 0, 0, 0, 0, 0, 1, 0, 1},
{
    
    1, 0, 1, 1, 1, 0, 0, 0, 1},
{
    
    1, 1, 0, 0, 0, 0, 1, 0, 0},
{
    
    1, 1, 1, 1, 1, 1, 1, 1, 1}
};
public static void main(String[] args) {
    
    
go(enterX, enterY);//开始走迷宫,跟上???冲冲冲
for (String path : stack) {
    
    //输出结果
System.out.println(path);
}
}
private static boolean go(int x, int y) {
    
    //开始go!go!go!
stack.push("(" + x + "," + y + ")");//迈出了第一步 yes!
visited[x][y] = true;//标记当前走过
if (x == exitX && y == exitY) {
    
    //你走出了迷宫yyds
return true;
}
for (int i = 0; i < direction.length; i++) {
    
    //四个方向去走
int newX = x + direction[i][0];//行
int newY = y + direction[i][1];//列
if (isInArea(newX, newY) && isRoad(newX, newY) && !visited[newX][newY]) {
    
    //如果当前是路,没有被走过,在范围之内(别跑偏了。。)
if (go(newX, newY)) {
    
    //如果走得通继续走
return true;//返回true
}
}
}
stack.pop();//当前位置走不通记得弹栈,就是回去
return false;//返回走不通
}
private static boolean isRoad(int newX, int newY) {
    
    //判断当前是否为路
return maze[newX][newY] == 0;
}
private static boolean isInArea(int newX, int newY) {
    
    //越界了
return newX >= 0 && newX < 9 && newY >= 0 && newY < 9;
}
private static void print(int[][] board) {
    
    //打印路
for (int i = 0; i < board.length; i++) {
    
    
for (int j = 0; j < board[i].length; j++) {
    
    
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}

5.数独
再给老铁们加一个数独!!!

import java.util.Scanner;
/*
005300000
800000020
070010500
400005300
010070006
003200080
060500009
004000030
000009700
*/
public class Sudoku {
    
    
public static void main(String[] args) {
    
    
int[][] board = new int[9][9];
Scanner scanner = new Scanner(System.in);
for (int i = 0; i < board.length; i++) {
    
    //手动输入数独,0表示空位。。
//建议不要随便输入,因为有可能你输的数独无解
String line = scanner.nextLine();
for (int j = 0; j < line.length(); j++) {
    
    
board[i][j] = Integer.parseInt(line.charAt(j) + "");
}
}
solve(0, 0, board);//开始数独游戏
}
private static void solve(int row, int col, int[][] board) {
    
    //row行 col列 board
if (row == 9) {
    
    //当走到最后一行表示结束
print(board);//打印结果
} else {
    
    
if (board[row][col] == 0) {
    
    //当前位置没有放任何数字
for (int num = 1; num <= 9; num++) {
    
    //依次尝试1-9,这里的循环表示存放的数字
if (!isExist(row, col, num, board)) {
    
    //如果num已经存在行或者列,或者3x3的区域里,就换其他数字
board[row][col] = num;//能放
solve(row + (col + 1) / 9, (col + 1) % 9, board);//下一行开始放
}
board[row][col] = 0;//回溯,当前位置放不了,需要回溯
}
} else {
    
    
solve(row + (col + 1) / 9, (col + 1) % 9, board);//当前位置已经放了,下一个位置开始放
}
}
}
private static boolean isExist(int row, int col, int num, int[][] board) {
    
    //判断是否能放
for (int c = 0; c < 9; c++) {
    
    
if (board[row][c] == num) {
    
    
return true;
}
}
for (int r = 0; r < 9; r++) {
    
    
if (board[r][col] == num) {
    
    
return true;
}
}
int rowMin = 0;
int colMin = 0;
int rowMax = 0;
int colMax = 0;
if (row >= 0 && row <= 2) {
    
    
rowMin = 0;
rowMax = 2;
}
if (row >= 3 && row <= 5) {
    
    
rowMin = 3;
rowMax = 5;
}
if (row >= 6 && row <= 8) {
    
    
rowMin = 6;
rowMax = 8;
}
if (col >= 0 && col <= 2) {
    
    
colMin = 0;
colMax = 2;
}
if (col >= 3 && col <= 5) {
    
    
colMin = 3;
colMax = 5;
}
if (col >= 6 && col <= 8) {
    
    
colMin = 6;
colMax = 8;
}
for (int r = rowMin; r <= rowMax; r++) {
    
    
for (int c = colMin; c <= colMax; c++) {
    
    
if (board[r][c] == num) {
    
    
return true;
}
}
}
return false;
}
private static void print(int[][] board) {
    
    
for (int i = 0; i < board.length; i++) {
    
    
for (int j = 0; j < board[i].length; j++) {
    
    
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}
}

总结

一口气刷完三道类似的题目,会发现其中的奥秘!!不难看出三道题目解法上大同小异都是采用,深度优先算法,其遍历都是由四个方向进行,每个方向记录一次结果。特别注意题目锁定的对象,通常情况下题目要求的结果是什么?那么作用对象就是该结果!意思就是每次深度优先算法考虑的对象,例如求最大岛屿面积,重点是形成岛屿并记录岛屿数。可能其中不同的一点是迷宫,因为题目限定了出入口,如果采用以上做法的话,会是所用路都遍历出来,而不能找到真正的出路。所以迷宫当中涉及了回溯和栈的树结构,走到最后发现不是出走就回退,直到找到出走的路。

猜你喜欢

转载自blog.csdn.net/cout_s/article/details/120122372