Leetcode 694. 不同岛屿的数量 中等 回溯 岛屿问题

694. 不同岛屿的数量

题目:

给定一个非空 01 二维数组表示的网格,一个岛屿由四连通(上、下、左、右四个方向)的 1 组成,你可以认为网格的四周被海水包围。

请你计算这个网格中共有多少个形状不同的岛屿。两个岛屿被认为是相同的,当且仅当一个岛屿可以通过平移变换(不可以旋转、翻转)和另一个岛屿重合。

 

示例 1:

11000
11000
00011
00011
给定上图,返回结果 1 。

 

思路:

labuladong

比如题目输入下面这个二维矩阵:

其中有四个岛屿,但是左下角和右上角的岛屿形状相同,所以不同的岛屿共有三个,算法返回 3。

很显然我们得想办法把二维矩阵中的「岛屿」进行转化,变成比如字符串这样的类型,然后利用 HashSet 这样的数据结构去重,最终得到不同的岛屿的个数。

如果想把岛屿转化成字符串,说白了就是序列化,序列化说白了就是遍历嘛,前文 二叉树的序列化和反序列化 讲了二叉树和字符串互转,这里也是类似的。

首先,对于形状相同的岛屿,如果从同一起点出发,dfs 函数遍历的顺序肯定是一样的。

因为遍历顺序是写死在你的递归函数里面的,不会动态改变:

所以,遍历顺序从某种意义上说就可以用来描述岛屿的形状,比如下图这两个岛屿:

假设它们的遍历顺序是:

下,右,上,撤销上,撤销右,撤销下

如果我用分别用 1, 2, 3, 4 代表上下左右,用 -1, -2, -3, -4 代表上下左右的撤销,那么可以这样表示它们的遍历顺序:

2, 4, 1, -1, -4, -2

你看,这就相当于是岛屿序列化的结果,只要每次使用 dfs 遍历岛屿的时候生成这串数字进行比较,就可以计算到底有多少个不同的岛屿了。

 

class Solution {
public:
    int numDistinctIslands(vector<vector<int>>& grid) {
        int m=grid.size();
        int n=grid[0].size();
        // 用于记录不同的路径字节序
        set<string> islands;
        for(int i=0;i<m;++i){
            for(int j=0;j<n;++j){
                // 遍历所有岛屿
                if(grid[i][j]==1){
                    string path;
                    // 起始点记为666
                    dfs(grid,i,j,666,path);
                    islands.insert(path);
                }
            }
        }
        return islands.size();
    }
    void dfs(vector<vector<int>>& grid,int i,int j,int dir,string& path){
        int m=grid.size();
        int n=grid[0].size();
        // 边界条件直接返回
        if(i<0||j<0||i>=m||j>=n||grid[i][j]==0){
            return ;
        }
        // 已遍历的岛屿置0
        grid[i][j]=0;
        // 记录进入路径
        path+=to_string(dir);
        path+=',';
        // 按顺序进入四边联通点
        dfs(grid,i,j+1,1,path);
        dfs(grid,i,j-1,2,path);
        dfs(grid,i+1,j,3,path);
        dfs(grid,i-1,j,4,path);
        // 记录退出途径
        path+=to_string(-dir);
        path+=',';
    }
};

 

posted @ 2022-05-30 16:14  鸭子船长  阅读(106)  评论(0编辑  收藏  举报