两种方式处理已经访问过的节点:一种是用visited存储已经访问过的1;另一种是通过改变原始数值的值,比如将1改成-1,这样小于等于0的都会停止。
Number of Islands 用了第一种方式,Number of Distinct Islands用了第二种方式
注意:如果采用更改原数组的方式,一定要注意加引用!!!
Number of Islands变种,假设给的矩阵四周都是陆地,和陆地相连不算island。
方法:与Number of Islands的search函数是一样的,只是需要进行预处理 ,即先把与四周的1相连的全变成0,然后再做与Number of Islands一样的操作
200. Number of Islands
个人理解dfs、bfs的时间复杂度都是 o(m*n)
时间复杂度o(m*n)
1.这种写法要改变原始输入数组的值
错误版本:
条件判断顺序写错:grid[x][y] == '0' || x < 0 || x >= length || y < 0 || y >= width
这种写法要报数组越界的错误,因为grid[x][y]会先访问,实际上x、y这个时候可能就越界了,grid[x][y]必须放在这几个越界判断的后面
class Solution { public: int numIslands(vectorchar>>& grid) { int length = grid.size(); if(length <= 0) return 0; int width = grid[0].size(); int count = 0; for(int i = 0;i < length;i++){ for(int j = 0;j < width;j++){ if(grid[i][j] == '1'){ search(grid,i,j,length,width); count++; } } } return count; } void search(vector char>>& grid,int x,int y,int length,int width){ if(x < 0 || x >= length || y < 0 || y >= width || grid[x][y] == '0') return; grid[x][y] = '0'; search(grid,x-1,y,length,width); search(grid,x+1,y,length,width); search(grid,x,y-1,length,width); search(grid,x,y+1,length,width); } };
整体思路,从第一点开始找1,如果找到1,把所有的与这个1相连的1置为0,因为这些1与这个1属于同一个岛屿,用dfs去找把所有的1找到
https://blog.csdn.net/xudli/article/details/45912547
2.不改变原始输入的值,还是用dfs
class Solution { public: int numIslands(vectorchar>>& grid) { int width = grid.size(); if(width <= 0) return 0; int length = grid[0].size(); if(length <= 0) return 0; int count = 0; vector bool>> visited(width,vector<bool>(length,false)); for(int i = 0;i < width;i++){ for(int j = 0;j < length;j++){ if(grid[i][j] == '1' && visited[i][j] == false){ search(grid,i,j,visited); count++; } } } return count; } void search(vector char>> grid,int x,int y,vector bool>>& visited){ if(x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size() || grid[x][y] == '0' || visited[x][y] == true) return; visited[x][y] = true; search(grid,x-1,y,visited); search(grid,x+1,y,visited); search(grid,x,y-1,visited); search(grid,x,y+1,visited); } };
http://www.cnblogs.com/grandyang/p/4402656.html
bfs的两种方法:
方法一:
class Solution { public: int numIslands(vectorchar>>& grid) { int m = grid.size(); if(m <= 0) return 0; int n = grid[0].size(); if(n <= 0) return 0; queue int,int>> q; int res = 0; for(int i = 0;i < m;i++){ for(int j = 0;j < n;j++){ if(grid[i][j] == '1'){ q.push(make_pair(i,j)); res++; grid[i][j] = '0'; while(!q.empty()){ int x= q.front().first; int y = q.front().second; q.pop(); //grid[x][y] = '0'; for(auto dir : dirs){ int x_cur = x + dir[0]; int y_cur = y + dir[1]; if(x_cur < 0 || x_cur >= m || y_cur < 0 || y_cur >= n || grid[x_cur][y_cur] == '0') continue; q.push(make_pair(x_cur,y_cur)); grid[x_cur][y_cur] = '0'; } } } } } return res; } vector int>> dirs{{-1,0},{1,0},{0,-1},{0,1}}; };
方法二:
class Solution { public: int numIslands(vectorchar>>& grid) { int m = grid.size(); if(m <= 0) return 0; int n = grid[0].size(); if(n <= 0) return 0; vector bool>> visited(m,vector<bool>(n,false)); queue int,int>> q; int res = 0; for(int i = 0;i < m;i++){ for(int j = 0;j < n;j++){ if(grid[i][j] == '1' && visited[i][j] == false){ q.push(make_pair(i,j)); res++; visited[i][j] = true; while(!q.empty()){ int x = q.front().first; int y = q.front().second; q.pop(); for(auto dir : dirs){ int x_cur = x + dir[0]; int y_cur = y + dir[1]; if(x_cur < 0 || x_cur >= m || y_cur < 0 || y_cur >= n || grid[x_cur][y_cur] == '0' || visited[x_cur][y_cur] == true) continue; visited[x_cur][y_cur] = true; q.push(make_pair(x_cur,y_cur)); } } } } } return res; } vector int>> dirs{{-1,0},{1,0},{0,-1},{0,1}}; };
错误方法一:
class Solution { public: int numIslands(vectorchar>>& grid) { int m = grid.size(); if(m <= 0) return 0; int n = grid[0].size(); if(n <= 0) return 0; queue int,int>> q; int res = 0; for(int i = 0;i < m;i++){ for(int j = 0;j < n;j++){ if(grid[i][j] == '1'){ q.push(make_pair(i,j)); res++; while(!q.empty()){ int x= q.front().first; int y = q.front().second; q.pop(); grid[x][y] = '0'; for(auto dir : dirs){ int x_cur = x + dir[0]; int y_cur = y + dir[1]; if(x_cur < 0 || x_cur >= m || y_cur < 0 || y_cur >= n || grid[x_cur][y_cur] == '0') continue; q.push(make_pair(x_cur,y_cur)); } } } } } return res; } vector int>> dirs{{-1,0},{1,0},{0,-1},{0,1}}; };
这个错误的方法与方法一类似,但是这个方法会在大数组的时候报超时,这是因为每次进行将1变成0的操作都是在队列弹出之后,这样会导致许多重复计算。
如下图,从右下角的1开始遍历,会先遍历到左下角和右上角,这种情况没问题,但是当左下角和右上角都要遍历到左上角时,都会把左上角这个1加入队列进行一次计算,但是正确写法却不用。因为正确写法,在加入队列的时候就已经置为0了,下一次再访问到这个位置肯定不会再加入队列了。
694 Number of Distinct Islands
存储搜索到的每个岛的形状,利用set来保证存储的形状不一样。形状的存储是根据每个遍历节点到每个岛的左上点的x、y差值,所以用pair的形式。因为每个岛有多个点,所以用vector存储
用unordered_set编译会报错
class Solution { public: int numberofDistinctIslands(vectorint>> &grid) { int m = grid.size(); if(m <= 0) return 0; int n = grid[0].size(); if(n <= 0) return 0; set int,int>>> shapes; for(int i = 0;i < m;i++){ for(int j = 0;j < n;j++){ if(grid[i][j] == 1){ vector int,int>> shape; numberofDistinctIslands(grid,i,j,i,j,shape); shapes.insert(shape); } } } return shapes.size(); } void numberofDistinctIslands(vector int>>& grid,int x0,int y0,int i,int j,vector int,int>>& shape){ if(i < 0 || i >= grid.size() || j < 0 || j >= grid[0].size() || grid[i][j] <= 0) return; grid[i][j] = -1; shape.push_back({x0 - i ,y0 - j}); numberofDistinctIslands(grid,x0,y0,i - 1,j,shape); numberofDistinctIslands(grid,x0,y0,i + 1,j,shape); numberofDistinctIslands(grid,x0,y0,i,j - 1,shape); numberofDistinctIslands(grid,x0,y0,i,j + 1,shape); } };
695. Max Area of Island
自己写的
记录每个岛屿的节点个数就好了。每次大的循环是一个岛屿,每次递归是一个节点,所以在每次递归的时候++就好了,然后比较所有岛屿中最大的
class Solution { public: int maxAreaOfIsland(vectorint>>& grid) { int m = grid.size(); if(m <= 0) return 0; int n = grid[0].size(); if(n <= 0) return 0; int max_num = 0; for(int i = 0;i < m;i++){ for(int j = 0;j < n;j++){ if(grid[i][j] == 1){ int num = 0; maxAreaOfIsland(grid,i,j,num); if(num > max_num) max_num = num; } } } return max_num; } void maxAreaOfIsland(vector int>>& grid,int i,int j,int& num){ if(i < 0 || i >= grid.size() || j < 0 || j >= grid[0].size() || grid[i][j] <= 0) return; grid[i][j] = -1; num++; maxAreaOfIsland(grid,i - 1,j,num); maxAreaOfIsland(grid,i + 1,j,num); maxAreaOfIsland(grid,i,j - 1,num); maxAreaOfIsland(grid,i,j + 1,num); } };
130. Surrounded Regions
在4条边上的O肯定不会被包围。
在4条边上找O,然后寻找这个O的岛屿,然后把这4条边能生成的岛屿全变成$,最后再将O变成X,$变成O。
这里第一个代码换了一种迭代的写法,每次判断的依据是以下一个节点为依据,后一个代码是自己经常使用的迭代写法,都是可以的。
class Solution { public: void solve(vectorchar> >& board) { int m = board.size(); if(m <= 0) return; int n = board[0].size(); if(n <= 0) return; for(int i = 0;i < m;i++){ for(int j = 0;j < n;j++){ if((i == 0 || i == board.size() -1 || j == 0 || j == board[0].size() - 1) && board[i][j] == 'O') solve(board,i,j); } } for(int i = 0;i < m;i++){ for(int j = 0;j < n;j++){ if(board[i][j] == 'O') board[i][j] = 'X'; if(board[i][j] == '$') board[i][j] = 'O'; } } return; } void solve(vector char> >& board,int i,int j){ board[i][j] = '$'; if(i >= 1 && board[i-1][j] == 'O') solve(board,i - 1,j); if(i < board.size() - 1 && board[i+1][j] == 'O') solve(board,i + 1,j); if(j >= 1 && board[i][j-1] == 'O') solve(board,i,j - 1); if(j < board[0].size() - 1 && board[i][j+1] == 'O') solve(board,i,j + 1); return; } };
自己最常写的。
class Solution { public: void solve(vectorchar> >& board) { int m = board.size(); if(m <= 0) return; int n = board[0].size(); if(n <= 0) return; for(int i = 0;i < m;i++){ for(int j = 0;j < n;j++){ if((i == 0 || i == board.size() -1 || j == 0 || j == board[0].size() - 1) && board[i][j] == 'O') solve(board,i,j); } } for(int i = 0;i < m;i++){ for(int j = 0;j < n;j++){ if(board[i][j] == 'O') board[i][j] = 'X'; if(board[i][j] == '$') board[i][j] = 'O'; } } return; } void solve(vector char> >& board,int i,int j){ if(i < 0 || i >= board.size() || j < 0 || j >= board[0].size() || board[i][j] != 'O') return; board[i][j] = '$'; solve(board,i - 1,j); solve(board,i + 1,j); solve(board,i,j - 1); solve(board,i,j + 1); } };
并查集:
是不是在一个集合里面
指向自己的优化
hash-map就可以解决
操作:1.查询
非递归比递归好。每次递归调用,程序保存了上一次调用的结果
集合代表
2.合并
434. Number of Islands II(lintcode)
https://www.cnblogs.com/grandyang/p/5190419.html
n代表行数、m代表列数,x是在行上的位置,y是在列上的位置,所以在roots中的坐标应该是:m * x + y
roots表示所有节点所属于的根,即属于哪一个集合,初始为-1表示没有属于任何一个集合。
count表示集合个数,如果本身是一个重来没属于任何结合的位置,遍历到的时候就需要 count++。
核心思路:两个相邻的位置不属于同一个集合,这表明需要更新集合。
class Solution { public: /** * @param n: An integer * @param m: An integer * @param operators: an array of point * @return: an integer array */ vector<int> numIslands2(int n, int m, vector&operators) { // write your code here vector<int> result; int count = 0; vector int>> dirs{{0, -1}, {-1, 0}, {0, 1}, {1, 0}}; vector<int> roots(m*n,-1); for(int i = 0;i < operators.size();i++){ int x = operators[i].x; int y = operators[i].y; int index = m * x + y; if(roots[index] == -1){ roots[index] = index; count++; } for(auto dir : dirs){ int dx = x + dir[0]; int dy = y + dir[1]; int cur_index = m * dx + dy; if(dx < 0 || dx >= n || dy < 0 || dy >= m || roots[cur_index] == -1) continue; int p = findroot(roots,cur_index),q = findroot(roots,index); if(p != q){ roots[p] = q; count--; } } result.push_back(count); } return result; } int findroot(vector<int> roots,int index){ int root = roots[index]; while(root != index){ index = root; root = roots[index]; } return root; } };
178. Graph Valid Tree
https://www.cnblogs.com/grandyang/p/5257919.html
这道题给了我们一个无向图,让我们来判断其是否为一棵树,我们知道如果是树的话,所有的节点必须是连接的,也就是说必须是连通图,而且不能有环,所以我们的焦点就变成了验证是否是连通图和是否含有环。
这个题与Number of Islands II的查找操作有点不太一样,Number of Islands II中根节点存储的是自己的下标,这个题的根节点存储的是-1,所以find函数不太一样。
class Solution { public: /** * @param n: An integer * @param edges: a list of undirected edges * @return: true if it's a valid tree, or false */ bool validTree(int n, vectorint>> &edges) { // write your code here vector<int> roots(n,-1); for(int i = 0;i < edges.size();i++){ int p = findRoot(roots,edges[i][0]); int q = findRoot(roots,edges[i][1]); if(p == q) return false; roots[p] = q; } return edges.size() == n-1; } int findRoot(vector<int> roots,int index){ while(roots[index] != -1) index = roots[index]; return index; } };