递归与回溯算法整理(二)

	这是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;    
    }
};

我当时面试的题目:是求最大的一块岛屿面积: 其实整体思路是一样的,稍微一丢丢差距就是求最大的一块岛屿的面积。

那么怎么做?
简单: 在递归时同时传入一个参数,记录当前岛屿的面积, 每次递归返回后,判断要不要更新最大岛屿面积,即可 。

695. 岛屿的最大面积

给定一个包含了一些 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;    
    }
};

463. 岛屿的周长

这个就先对而言简单多了 ,我就不实现了。

N皇后问题–递归与回溯法

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;
    }
};

你可能感兴趣的:(动态规划,数据结构,递归与回溯算法,N皇后,岛屿,DFS)