岛屿问题学习

岛屿问题是一类图的遍历问题,需要在图的结构上进行DFS(深度优先搜索),虽然理论懂但是上手就抓瞎,岛屿问题是网格DFS的代表问题,正好结合讲解学习一下,主要参考这里
典型的岛屿问题有如下:
岛屿数量
岛屿周长
岛屿的最大面积

网格搜索问题

网格搜索问题就是在由 m × n m\times n m×n个小方格组成的网格上进行搜索。在岛屿类问题中,认为网格中数字为 0 0 0的为海洋、数字为 1 1 1的是陆地,当相邻的格子数字相同时,就看作就一片连起来的岛屿。
在这里插入图片描述

根据图就可以引申出岛屿面积、岛屿周长等一系列问题,而这类问题都可以使用DFS遍历的方法来解答。

网格的DFS结构

首先,DFS是深度优先搜索,应用递归的思想,他的两个关键点在于:

1. 访问子结点
2.判断终止条件

  • 访问子结点需要确定子结点有哪些。对于二叉树的一个结点root来说,子结点就是它的左子结点root.left和右子节点root.right。而对于网格类问题,一个结点root的子结点是包括上下左右的相邻网格root.leftroot.rightroot.uproot.down,因此需要注意递归调用的结点定义不同。
  • 判断终止条件。对于一般二叉树而言,递归的终止条件为root == null。这时则需要向上返回,防止空指针异常。而对于网格类问题,他的终止条件应当是判断——此结点是否在网格范围内。如图所以,这是一个5x5的网格grid,则若结点grid[r][c]的r和c的不在5x5的范围内,则说明该结点不存在,需要返回递归。
    讲上述的DFS框架转换为代码语言,则有:
//判断是否为可用岛屿
func inAera(grid [][]int,r int,c int) bool{
	if r>=0 && r<=len(grid) && c>=0 && c<=len(grid[0]){
		return true
	}
	return false
}

func dfs(grid [][]int,r int,c int)  {
	//超出网格范围,直接返回
	if !inAera(grid,r,c){
		return;
	}
	//访问所有子结点
	dfs(grid,r-1,c)
	dfs(grid,r+1,c)
	dfs(grid,r,c+1)
	dfs(grid,r,c-1)
}
避免重复遍历

设置一个标签来确定该结点是否被访问过

//判断是否为可用岛屿
func inAera(grid [][]int,r int,c int) bool{
	if r>=0 && r<=len(grid) && c>=0 && c<=len(grid[0]){
		return true
	}
	return false
}

func dfs(grid [][]int,r int,c int)  {
	//超出网格范围,直接返回
	if !inAera(grid,r,c){
		return;
	}
	//判断是否为岛屿网格,不是则回退
	if grid[r][c]!=1{
		return
	}
	//将访问过的结点设为2
	grid[r][c]=2
	//访问所有子结点
	dfs(grid,r-1,c)
	dfs(grid,r+1,c)
	dfs(grid,r,c+1)
	dfs(grid,r,c-1)
}

有了基本框架,看一看具体问题。

一、岛屿数量问题在这里插入图片描述

基于已有的dfs框架,结合具体问题分析。从终止条件看:
1、当网格非法时,回退返回;
2、当网格被访问过时,回退返回;
在进行岛屿dfs遍历时,将连片的岛屿置为2,表示这是一个被访问过的岛屿

//判断是否为可用岛屿
func inAera(grid [][]byte,r int,c int) bool{
	if r>=0 && r<len(grid) && c>=0 && c<len(grid[0]){
		return true
	}
	return false
}

func dfs(grid [][]byte,r int,c int){
	//超出网格范围,直接返回
	if !inAera(grid,r,c){
		return;
	}
	//判断是否为岛屿网格,不是则回退
	if grid[r][c]!='1'{
		return
	}
	//将访问过的结点设为2
	grid[r][c]='2'
	//访问所有子结点
	dfs(grid,r-1,c)
	dfs(grid,r+1,c)
	dfs(grid,r,c+1)
	dfs(grid,r,c-1)
}

func numIslands(grid [][]byte) int {
	if len(grid)==0{
		return 0
	}
	nums:=0
	for r:=0;r<len(grid);r++{
		for c:=0;c<len(grid[0]);c++ {
			if grid[r][c]=='1'{
				dfs(grid,r,c)
				nums++
			}
		}
	}
	return nums
}
二、岛屿周长问题

在这里插入图片描述

基于已有的dfs框架,结合具体问题分析。从终止条件看:
1、当网格非法时,回退返回;
2、当网格被访问过时,回退返回;
3、在进行岛屿dfs遍历时,将连片的岛屿置为2,表示这是一个被访问过的岛屿
4、如果一个陆地网格的子结点为水网格,则需要周长增加一

//判断是否为可用岛屿
func inAera(grid [][]int,r int,c int) bool{
	if r>=0 && r<len(grid) && c>=0 && c<len(grid[0]){
		return true
	}
	return false
}

func dfs(grid [][]int,r int,c int) int{
	//超出网格范围,说明在边界,周长+1
	if !inAera(grid,r,c){
		return 1;
	}
	//如果是水网格,说明是岛屿边界,周长+1
	if grid[r][c]==0{
		return 1
	}
	//如果网格已被访问过,则回退
	if grid[r][c]!=1{
		return 0
	}
	//将访问过的结点设为2
	grid[r][c]=2
	//访问所有子结点
	return dfs(grid,r-1,c) + dfs(grid,r+1,c) +dfs(grid,r,c+1) + dfs(grid,r,c-1)
}

func islandPerimeter(grid [][]int) int {
	if len(grid)==0{
		return 0
	}
	ans:=0
	for r:=0;r<len(grid);r++{
		for c:=0;c<len(grid[0]);c++ {
			if grid[r][c]==1{
				ans=dfs(grid,r,c)
			}
		}
	}
	return ans
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值