深度优先遍历DFS (岛屿问题)java

算法之深度优先遍历(DFS)

最近在学习DFS和BFS,所以做一些学习的笔记,这里是深度遍历。

首先,比较常见的深度遍历题目就是网格题(可抽象为二维数组)。在LeetCode中常见的是岛屿问题。

思想:
深度优先遍历的思想可以理解为:

  1. 找到一个起始点S
  2. 选择一个与S相邻的节点N1,并到达该节点
  3. 在以该N1未起始点,选择他的相邻节点N2
  4. …如此循环,直到最后有一个节点Nn没有相邻节点
  5. 此时,返回到上一个节点N(n-1),选择另外的相邻节点,继续步骤2-4
  6. 最后就会遍历完全部的节点

这里会有一个问题:第二部会访问N2的相邻节点,那么是不是会重新访问起始点S?这样不就死循环了吗?确实会这样,所以需要解决这个问题,解决办法就是将访问过的节点标记出来,这样就不会重复访问了

在这里插入图片描述
上图中,序号即为遍历的顺序,绿色的背景表示已经访问过,已访问的节点不再访问,所以可以避免重复访问的问题(图中没有展示回溯到上一层的过程)

模板:
DFS和BFS都有模板可以使用,所以有必要先将模板展示出来,此处展示的是网格题僧都遍历的模板,其他的都是异曲同工
岛屿中:0表示海洋,1表示陆地,2表示已经访问过
如:
1 1 1 0 1 0 0
0 0 0 1 1 1 0
1 0 0 0 0 0 0
0 0 0 0 0 0 1

// 深度遍历函数
void dfs(int[][] area, int x, int y){
	// 如果不在区域内,直接返回
	if(!inArea(area,x,y)){  
		return;
	}
	// 如果已经访问过,则直接返回,不在访问
	if(area[x][y] == 2){
		return;
	}
	//否则遍历其相邻的节点
	dfs(area, x-1, y);  // 上
    dfs(area, x, y+1);  // 右
    dfs(area, x+1, y);  // 下
    dfs(area, x, y-1);  // 左
}

// 判断是否在区域内
boolean inArea(int[][] area, int x, int y){
	// 再区域内的条件:x,y>0 同时x不超过最大行数,y不超过最大列数
	return x>=0 && x<area.length && y>=0 && y<area[0].length;
}


例题1:
1.leetcode200
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
输入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
输出:1
代码:

class Solution {
    public int numIslands(char[][] grid) {
     	// 岛屿数量
        int count=0; 
        // 遍历每一个点 
        for(int i=0; i<grid.length; i++){
            for(int j=0; j<grid[0].length; j++){
            	// 如果该点已被访问过直接跳过
                if(grid[i][j] != '1'){
                    continue;
                }
                count += dfs(grid,i,j);
            }
        }
        return count;
    }

    int dfs(char[][] area,int x, int y){
    	// 如果不在区域内
        if(!isArea(area,x,y)) {  
            return 0;
        }
		// 不是未访问的陆地
        if(area[x][y] != '1'){   
            return 0;
        }
		// 标记已访问
        area[x][y] = '2';

        dfs(area, x-1, y);  // 上
        dfs(area, x, y+1);  // 右
        dfs(area, x+1, y);  // 下
        dfs(area, x, y-1);  // 左
        return 1;
    }

    boolean isArea(char[][] area, int x, int y){
        return x>=0 && x<area.length && y>=0 && y<area[0].length;
    }
}

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

class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int max=0;          // 最大岛屿面积
        int square=0;       // 每块岛屿的面积
        for(int i=0; i<grid.length; i++){
            for(int j=0; j<grid[0].length; j++){
                if(grid[i][j] != 1){
                    continue;
                }
                square = dfs(grid,i,j,0);    // 当前岛屿面积,初始时,该岛屿面积为1
                max = max > square ? max : square;      // 获取最大岛屿面积
            }
        }
        return max;
    }

    /*
    最大岛屿面,需要新增传入参数:squ---当前已知该岛屿的面积
    */
    int  dfs(int[][] area, int x, int y, int squ){
        if(!inArea(area,x,y)){          // 是否在区域内
            return squ;                 // 不在直接返回传入时的面积
        }

        if(area[x][y] != 1){
            return squ;                  // 如果该点以访问,也是直接返回传入时的面积
        }

        area[x][y] = 2;                  // 以上两个条件都通过,则该点是"新大陆",岛屿面积+1
        squ++;

        squ = dfs(area,x-1,y,squ);       // 循环判断
        squ = dfs(area,x+1,y,squ);
        squ = dfs(area,x,y-1,squ);
        squ = dfs(area,x,y+1,squ);
        return squ;                      // 当该点走投无路时,及得到岛屿面积

    }

    boolean inArea(int[][] area, int x, int y){
        return x>=0 && x<area.length && y>=0 && y<area[0].length;
    }
}

此做法时间复杂度低,耗时少,但是空间复杂度较高

更多,待学习。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值