前端刷题:深度优先遍历 | 刷题打卡

175 阅读2分钟

一、题目描述:

面试题 16.19. 水域大小

你有一个用于表示一片土地的整数矩阵land,该矩阵中每个点的值代表对应地点的海拔高度。若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小,返回值需要从小到大排序。
示例:
输入:

[
  [0,2,1,0],
  [0,1,0,1],
  [1,1,0,1],
  [0,1,0,1]
]

输出:

[1,2,4]

提示:

0 < len(land) <= 1000 0 < len(land[i]) <= 1000

二、思路分析:

这道题很显然是很典型的深度优先遍历算法(DFS,Depth-First-Search),(当然也可以使用BFS,我们这里先不讨论)。对于这类题目,给的参数是个二维数组,然后让我们去查找里面符合条件元素的所有值,这个时候我们第一个要考虑的就是深度优先遍历了。虽然性能不一定是最好的,但是这种方法很大概率上可以算出答案。 深度优先遍历算法的核心思想是:穷尽所有的完整路径。解决的一般方法是:

  1. 我们先遍历数组找到符合条件的元素,然后以这些元素为中心递归遍历题目中符合条件的位置,题目中会有明显的限制条件,比如垂直水平位置,或者加上垂直方向,在遍历的时候对这些符合条件位置进行递归。
  2. 在递归的时候边界条件通常是二维数组的行头尾/列头尾,同时要注意在一个元素遍历完成之后需要及时的修改值,避免重复遍历
  3. 使用一个全局变量,将递归遍历的结果给保存起来,注意如果使用中间变量存储的时候每次遍历的时候要及时置空。

三、AC 代码:

这里晒一下我自己的代码,按照上面的规律来写,性能一般但是在面试的时候可以很快的得出结果

/**
 * @param {number[][]} land
 * @return {number[]}
 */
var pondSizes = function(land) {
    let row = land.length;
    let col = land[0].length;
    let res = []
    let temp = 0;
    const direct = [[1,0],[-1,0],[0,1],[0,-1],[1,1],[-1,-1],[1,-1],[-1,1]]
    const DFS = (land,i,j)=>{
        if(i<0||j<0||i>=row||j>=col) return;
        if(land[i][j] != 0) return;
        land[i][j]=1;
        temp++;
        for(let m = 0;m<direct.length;m++){
            DFS(land,i+direct[m][0],j+direct[m][1])
        }
    }
    for(let i = 0;i<row;i++){
        for(let j=0;j<col;j++){
            if(land[i][j]==0){
                DFS(land,i,j)
                res.push(temp)
                temp=0;
            }
        }
    }
    return res.sort()
};

四、总结:

经典的DFS算法题目通常都可以使用上面的思路来解答,可能存在的区别就是边界条件,所要查找的元素内容。

当然,DFS题目不止这一种类型,我们这里可以梳理一下:

  1. 我们需要递归查找的内容是二维数组,有的时候查找的内容是连续的,比如需要拼接字符串,这个时候我们遍历的时候需要保存一个状态,一般使用栈比较好,注意递归的时候及时出栈入栈
  2. 我们需要递归查找的是二叉树,这个时候其实是个递归查找,解题模版基本如下:
const DFS = (root){
	if(!root) return ;
	if(!root.left&&!root.right&&...){
		return;
	}
	DFS(root.left);
	DFS(root.right);
	...
}

当然这里说的模版并不能用来解决所有的此类题目,但是在遇到这种题目的时候使用模版可以第一时间找到思路,避免没有头绪。

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情