Leetcode 694. 不同岛屿的数量 中等 回溯 岛屿问题
694. 不同岛屿的数量
题目:
给定一个非空 01 二维数组表示的网格,一个岛屿由四连通(上、下、左、右四个方向)的 1 组成,你可以认为网格的四周被海水包围。
请你计算这个网格中共有多少个形状不同的岛屿。两个岛屿被认为是相同的,当且仅当一个岛屿可以通过平移变换(不可以旋转、翻转)和另一个岛屿重合。
示例 1:
11000
11000
00011
00011
给定上图,返回结果 1 。
思路:
比如题目输入下面这个二维矩阵:
其中有四个岛屿,但是左下角和右上角的岛屿形状相同,所以不同的岛屿共有三个,算法返回 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+=','; } };
联系方式:emhhbmdfbGlhbmcxOTkxQDEyNi5jb20=