欢迎访问我的blog http://www.codinglemon.cn/
0. 深度优先搜索(DFS)
先简单了解一下DFS,其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只访问一次。
DFS思修基于递归思想,通过递归的形式来缩小问题规模,把一件事分割成若干个相同的小事,逐步完成。
深度优先搜索的步骤分为 1.递归下去 2.回溯上来。顾名思义,深度优先,则是以深度为准则,先一条路走到底,直到达到目标。这里称之为递归下去。
否则既没有达到目标又无路可走了,那么则退回到上一步的状态,走其他路。这便是回溯上来。
下面直接看题吧。
1. LeetCode 733. 图像渲染
1.1 题目描述
有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间。
给你一个坐标 (sr, sc) 表示图像渲染开始的像素值(行 ,列)和一个新的颜色值 newColor,让你重新上色这幅图像。
为了完成上色工作,从初始坐标开始,记录初始坐标的上下左右四个方向上像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应四个方向上像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为新的颜色值。
最后返回经过上色渲染后的图像。
示例 1:
输入:
image = [[1,1,1],[1,1,0],[1,0,1]]
sr = 1, sc = 1, newColor = 2
输出: [[2,2,2],[2,2,0],[2,0,1]]
解析:
在图像的正中间,(坐标(sr,sc)=(1,1)),
在路径上所有符合条件的像素点的颜色都被更改成2。
注意,右下角的像素没有更改为2,
因为它不是在上下左右四个方向上与初始点相连的像素点。
注意:
- image 和 image[0] 的长度在范围 [1, 50] 内。
- 给出的初始点将满足 0 <= sr < image.length 和 0 <= sc < image[0].length。
- image[i][j] 和 newColor 表示的颜色值在范围 [0, 65535]内。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/flood-fill
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
1.2 解题思路
这个题的大概意思就是通过题目给定的初始点去找在初始点上下左右都相同值的点,如果该点值与初始点的值相同,则再以该点为基准,找出上下左右与该点值相同的点…一直这么扩散下去。
其实跟标题含义一样,我们用画图工具或者ps的油漆桶去填充某一块区域的时候,我们通过点击区域中的某一个点,程序会根据这个点扩散的去找与该点颜色相同且相邻的所有像素点,然后修改为指定的颜色。
解题步骤如下:
- 从给定的起点开始,进行深度优先搜索。
- 每次搜索到一个方格时,如果其与初始位置的方格颜色相同,就将该方格的颜色更新,以防止重复搜索
- 如果不相同,则进行回溯。
看解题步骤觉得回溯比较难,其实只要遇到边界,不管他就好了,因为在边界之前的所有情况都已经处理过了。
tips:深度优先遍历一般都使用递归去解决问题。
1.3 解题代码
class Solution {
public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
int oldColor = image[sr][sc];
if(oldColor != newColor){
replaceOldColor(image,sr,sc,oldColor,newColor);
}
return image;
}
public void replaceOldColor(int[][] image, int sr, int sc,int oldColor,int newColor){
if(sr < 0 || sc < 0 || sr>= image.length || sc >=image[0].length || image[sr][sc] != oldColor){
return;
}
image[sr][sc] = newColor;
//上
replaceOldColor(image,sr-1,sc,oldColor,newColor);
//下
replaceOldColor(image,sr+1,sc,oldColor,newColor);
//左
replaceOldColor(image,sr,sc-1,oldColor,newColor);
//右
replaceOldColor(image,sr,sc+1,oldColor,newColor);
}
}
这里编写找寻上下左右的点的代码不太优美,下面的题解中有比较优美的解决办法。
2. LeetCode 695. 岛屿的最大面积
2.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。
注意: 给定的矩阵grid 的长度和宽度都不超过 50。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/max-area-of-island
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
2.2 解题思路
这个题跟前面的题其实解题思路类似,只不过他的基准点是从坐标(0,0)开始顺序向下一直找的。聪明的同学就会发现一个问题,如果我们像上一个题这么写,那执行时间不是爆炸?**因为每设置一个新的基准点,就通过这个基准点向4个方向去找与他相邻的1,肯定会有非常非常多重复的查找情况。**比如:
现在有[[1,1],[1,1]]这样一个数组,那么如果按上一个方法去找,每个1都会去查找一次,虽然最后结果返回的是4,但是查找重复了3次,这还只是一个2X2的数组,如果是50X50的数组,查找重复的次数将大大增长。
那么怎么去解决这个问题呢?其实可以在每次查找到值为1的点后,将该点的值置为0,表明这个地方已经查找过了,下次再查找相同的区块,就不会重复了。
这里的置0也可以这么理解,也就是说我查找到某一个岛屿并计算出他的面积之后,将该岛屿从地图上直接剔除(因为标0就默认该区域无岛屿),再去计算其他岛屿,防止重复计算。
解题步骤如下:
- 从给定点开始,先判断给定点是否超出边界或给定点的值是否为1,如果不为1或超出边界则返回0
- 若给定点值为1,则将给定点的值置为0,将结果answer置为1,且递归的查找给定点上下左右4个方向上的点,将递归结果累加到answer上,然后返回answer值。
- 取当前answer值与递归方法返回的answer值中的较大值,循环每个基准点结束后,返回结果。
2.3 解题代码
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int ans = 0;
for(int i=0;i<grid.length;i++){
for(int j=0;j<grid[0].length;j++){
ans = Math.max(ans,findOne(grid,i,j));
}
}
return ans;
}
public int findOne(int[][] grid,int i,int j){
if(i <0 || j< 0|| i==grid.length||j == grid[0].length||grid[i][j] != 1){
return 0;
}
//通过两个数组的组合来标定4个方向
int[] di = {0,0,1,-1};
int[] dj = {1,-1,0,0};
grid[i][j] = 0;
int ans = 1;
for(int k=0;k<4;k++){
int next_i = di[k]+i;
int next_j = dj[k]+j;
ans += findOne(grid,next_i,next_j);
}
return ans;
}
}
这里可以看到,通过两个数组的组合,可以标定4个方向,代码更加简洁。
其他类似题目,可以用作练习: