这是leetcode 上的一个经典的习题: 也是我面试伴鱼时碰到的一个问题(汗~ 当初没好好刷题)
https://leetcode-cn.com/problems/number-of-islands/
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
其实这是一个 典型的递归回溯算法。 为什么这么说呢?
我们的目的就是遍历一下所有连着的岛屿,并将其染色
每次递归回来时: 就说明一块连续的岛屿走完了。
但是: 题目给的二维数组:也就是说:存在若干岛屿
那么 就需要遍历了。 那么怎么保证递归的时间复杂度呢?
我们可以开辟二维数组来进行标记。
走过的岛屿用true 标记
其实这同时是一个典型的DFS算法解题套路
class Solution {
private:
vector<vector<bool>> memo;
int next[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};
int m,n; //岛屿 长、宽
private:
bool InArea(int a, int b)
{
return a>=0 && a<m && b>=0 && b<n;
}
void subQuestion(vector<vector<char>>& grid, int x, int y)
{
for(int i=0; i < 4; ++i)
{
int newx = x + next[i][0];
int newy = y + next[i][1];
if(InArea(newx,newy) && grid[newx][newy]=='1'
&& !memo[newx][newy]) // 在地图上、是陆地、并且没访问过
{
memo[newx][newy] = true;//访问了
subQuestion(grid, newx, newy);
}
}
}
public:
int numIslands(vector<vector<char>>& grid) {
int res = 0;//总数量
m = grid.size();
if( m == 0) return 0;
n = grid[0].size();
memo = vector<vector<bool>>(m, vector<bool>(n,false));
for(int i=0; i<m; ++i)
for(int j=0; j<n; ++j)
if (grid[i][j]=='1' && !memo[i][j] && ++res)//是陆地、也没来过:++res
//我是为了让代码看着简短, 当然你可以把res提出来
//我为什么放进去,原理就是 && 是否引起短路
//memo[i][j]可有可无: 为啥?这就是回溯/递归
subQuestion(grid, i, j);//来一次
return res;
}
};
我当时面试的题目:是求最大的一块岛屿面积: 其实整体思路是一样的,稍微一丢丢差距就是求最大的一块岛屿的面积。
那么怎么做?
简单: 在递归时同时传入一个参数,记录当前岛屿的面积, 每次递归返回后,判断要不要更新最大岛屿面积,即可 。
给定一个包含了一些 0 和 1 的非空二维数组 grid 。
一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)
class Solution {
private:
vector<vector<bool>> memo;
int next[4][2] = {{-1,0},{0,1},{1,0},{0,-1}};
int m,n; //岛屿 长、宽
private:
bool InArea(int a, int b)
{
return a>=0 && a<m && b>=0 && b<n;
}
void subQuestion(vector<vector<int>>& grid, int x, int y,int& area)
{
//cout<<++k<
for(int i=0; i < 4; ++i)
{
int newx = x + next[i][0];
int newy = y + next[i][1];
if(InArea(newx,newy) && grid[newx][newy]==1
&& !memo[newx][newy]) // 在地图上、是陆地、并且没访问过
{
memo[newx][newy] = true;//访问了
subQuestion(grid, newx, newy, ++area);
}
}
}
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int res = 0;//总数量
m = grid.size();
if( m == 0) return 0;
n = grid[0].size();
memo = vector<vector<bool>>(m, vector<bool>(n,false));
for(int i=0; i<m; ++i)
for(int j=0; j<n; ++j)
if (grid[i][j]==1 && !memo[i][j])//是陆地、也没来过:++res
//我是为了让代码看着简短, 当然你可以把res提出来
//我为什么放进去,原理就是 && 是否引起短路
{
int tmp=1;
memo[i][j] = true;
subQuestion(grid, i, j, tmp);//来一次
res = res > tmp? res : tmp;
}
return res;
}
};
这个就先对而言简单多了 ,我就不实现了。
https://leetcode-cn.com/problems/n-queens/
注意的是 保存记录数据:
列 , 两条对角线
在注意的是 两条对角线有什么规律。
左上角为 0,0 右下角为 n,n
class Solution {
private:
vector<vector<string>> res;
private:
//index 代表行
void subQuestion(vector<bool>& col, vector<bool>& f1, vector<bool>& f2, vector<string>& tmp, int index, int n)
{
if (index == n)
res.push_back(tmp);
for(int i=0; i<n; ++i)//列
if(!col[i] && !f1[n-1+index-i] && !f2[index+i])
{
col[i]=f1[n-1+index-i]= f2[index+i] = true;
tmp[index][i] = 'Q';//存放
subQuestion(col, f1, f2, tmp, index+1, n); //成没成功都需要回溯:我们需要存多组数据
tmp[index][i] = '.';//回溯
col[i]=f1[n-1+index-i]=f2[index+i] = false;
}
}
public:
// 分析返回值类型便知道了,每一组一个vector,也就是用string替换了一个vector
vector<vector<string>> solveNQueens(int n) {
res.clear();
//一行一个 不用考虑行
vector<bool> col(n,false);//列 n列
vector<bool> f1(2*n,false);//主对角线:n-1 + x-y恒定
vector<bool> f2(2*n,false);//副对角线 x+y 恒定
vector<string> tmp(n, string(n,'.'));
subQuestion(col, f1, f2, tmp, 0, n);
return res;
}
};