【递归、搜索与回溯】综合练习四

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.单词搜索

题目链接:79. 单词搜索

题目分析:

在这里插入图片描述

给一个mxn board二维数组,让在数组中找是否存在字符串单词 word,注意只能上下左右去找,不能斜着找。

算法原理:
我们在这个矩阵中依次找到word里面的所有字符。首先在这个矩阵里面先一行一行的扫描找到word里面第一个字符,当找到第一个字符时,就从这个字符开始去匹配其他剩余的字符,注意只能上下左右去匹配,如果上下左右都不匹配说明这个位置是一次失败的匹配。那就在去找其他位置能匹配的第一个字符。如果找到还是上下左右去匹配,如果能匹配成功说明这个矩阵有这个单词。

在这里插入图片描述

如果把这道题抽象一下,你会发现刚刚的过程j就是解决迷宫常用的方式,深度优先遍历。如果走不通就回溯一下再往下走,直到找到一个正确结果为止。

在这里插入图片描述
接下来我们考虑递归函数如何设计,从某个节点开始上下左右匹配下一个字符,所以则函数体干的就是给一个位置然后上下左右去匹配下一个字符。这个参数首先把这个board给我,然后第一个字符的位置,然后给我要匹配字符串中下一个字符的位置。dfs(board,i,j,s,pos),注意看这个决策树我们从一个位置走可能走失败,上面调用dfs的得知道是找成功还是失败,所以dfs有一个bool类型的返回值,
bool dfs(board,i,j,s,pos) 失败就去另一条路径。剪枝就是那一个位置能匹配就去走那一个位置。回溯 一条路径找失败往上走就是回溯,往下走之前你弄了什么,反着来就可以了。

下面是细节问题:二维矩阵搜索中,经常要注意的细节。
不能走重复的路
就比如下面的这个位置,一直会是重复的。之前用过的下一次不能在找了。
在这里插入图片描述
这里我们有两种方法规避。

  1. 搞一个bool类型的跟原始数组大小一样的二维数组 bool visit[][]。用这个数组来标记当前这个位置是否被使用过。使用过就把这个位置标记成true。然后到下一层的时候就考虑一下上一个位置是否是ture,是就不走,不是就可以走。
  2. 修改原始矩阵的值。 比如说把被使用的位置的值修改成*,面试时还是要问一下能否修改,能修改再用这种方法。但是还有一个问题你修改完去往下一层,然后再回来的时候你要把被修改的值在还原回来。

这里在写代码去上下左右匹配的时候有两种写法:

  1. 可以分别写上下左右四个函数去匹配。
  2. 我们可以用向量的方式,定义上下左右四个位置。 然后仅需一次for循环就可以把上下左右都考虑进去了。

单独看这个i,上下左右就是在原始的i基础上要么不变,要么+1,要么-1,所以可以搞一个数组 int dx[4]={0,0,1,-1}; 这个表示i接下来可能的偏移量。同理j也有四个偏移量 int dy[4]={-1,1,0,0}; 然后这两个就可以上下凑一起使用。
在这里插入图片描述

class Solution {
    bool check[7][7];
    int m,n;
public:
    bool exist(vector<vector<char>>& board, string word) {
        m=board.size(),n=board[0].size();

        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n; ++j)
            {
                if(board[i][j] == word[0])
                {
                    check[i][j]=true;
                    if(dfs(board,i,j,word,1)) return true;
                    check[i][j]=false;
                }
            }
        }
        return false;
    }

    int dx[4]={0,0,-1,1};
    int dy[4]={-1,1,0,0};

    bool dfs(vector<vector<char>>& board,int i,int j,string& s,int pos)
    {
        if(pos == s.size()) return true;

        for(int k = 0; k < 4; ++k)
        {
            int x=i+dx[k],y=j+dy[k];

            if(x >= 0 && x < m && y >= 0 && y < n && check[x][y] == false && board[x][y] == s[pos])
            {
                check[x][y]=true;
                if(dfs(board,x,y,s,pos+1)) return true;
                check[x][y]=false;
            }
        }
        return false;

    }

};

2.黄金矿工

题目链接:1219. 黄金矿工

题目分析:

在这里插入图片描述

这道题和上面单词搜索就是同属于一类题,这里也是上下左右去搜索,不能走重复路。当天这里还有要求不能开采为0的单元格。

算法原理:
我们还是先去遍历找到不为0的单元格,然后从这个位置开始上下左右去递归,注意已经被选过的下次不能在选,可以弄一个原数组大小bool类型二维数组,当然也可以修改原始的值。其余的几乎和上面题一模一样。
在这里插入图片描述
修改原始的值

class Solution {
    int m,n,ret;
public:
    int getMaximumGold(vector<vector<int>>& grid) {
        m=grid.size(),n=grid[0].size();

        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n ; ++j)
            {
                if(grid[i][j] != 0)
                {
                    int num=grid[i][j];
                    grid[i][j]=0;
                    dfs(grid,i,j,num);
                    grid[i][j]=num;
                }
            }
        }
        return ret;
    }

    int dx[4]={0,0,-1,1};
    int dy[4]={-1,1,0,0};

    void dfs(vector<vector<int>>& grid,int i,int j,int path)
    {
    	//每次递归进来就统计,不用硬找递归出口
        ret=max(ret,path);

        for(int k = 0; k < 4; ++k)
        {
            int x=i+dx[k],y=j+dy[k];
            if(x >= 0 && x < m && y >= 0 && y < n && grid[x][y] != 0)
            {
                int num=grid[x][y];
                grid[x][y]=0;
                dfs(grid,x,y,path+num);
                grid[x][y]=num;
            }
        }
    }
};
class Solution
{
	 bool vis[16][16];
	 int dx[4] = {0, 0, 1, -1};
	 int dy[4] = {1, -1, 0, 0};
	 int m, n;
	 int ret;
public:
	 int getMaximumGold(vector<vector<int>>& grid) 
	 {
		 m = g.size(), n = g[0].size();
		 for(int i = 0; i < m; i++)
		 {
			 for(int j = 0; j < n; j++)
			 {
				 if(grid[i][j])
				 {
					 vis[i][j] = true;
					 dfs(grid, i, j, grid[i][j]);
					 vis[i][j] = false;
				 }
			 }
		}
		 return ret;
    }
    
	void dfs(vector<vector<int>>& grid, int i, int j, int path)
	{
		ret = max(ret, path);
		for(int k = 0; k < 4; k++)
		{
			int x = i + dx[k], y = j + dy[k];
			if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y])
			{
				vis[x][y] = true;
				dfs(grid, x, y, path + grid[x][y]);
				vis[x][y] = false;
			}
		}
	}
};

3.不同路径 III

题目链接:980. 不同路径 III

题目分析:

在这里插入图片描述

这个棋盘,1代表开始,2代表结束,0表示可以走过的方格,-1表示障碍。
从开始到结束有很多的走法,但是仅有两种路径把所有0方格都通过一次。

在这里插入图片描述
算法原理:
我们策略就是把从1走到2所有合法的路径个数统计出来。我们可以先扫描出数组找到起始位置,然后从这个起始位置开始dfs,不管你怎么走的,只要走到2你这条路径是合法的就行了。如何判断合法特别简单,向下递归的时候用一个count变量记录一下从开始到结束所走的这条路径个数。当走到2的时候判断一下这个count和我实际要走的步数step是否一样,实际要走步数在dfs之前把先描述整个数组先统计这个数组0有多少个,然后在加上起始位置和结束位置就是实际要走的步数。如果走到2 count == step 就找到一条路径。不相等就不统计。然后继续暴搜直到把所有能到2的路径合法的统计出来。

class Solution {
    bool vis[21][21];
	 int dx[4] = {0, 0, 1, -1};
	 int dy[4] = {1, -1, 0, 0};
    int m,n;
    int ret,step;
public:
    int uniquePathsIII(vector<vector<int>>& grid) {
        m=grid.size(),n=grid[0].size();
        int beginx,beginy;

        // 统计0的个数 以及 开始的地方
        for(int i = 0; i < m; ++i)
        {
            for(int j = 0; j < n ; ++j)
            {
                if(grid[i][j] == 0) ++step;
                else if(grid[i][j] == 1) 
                {
                    beginx=i;
                    beginy=j;
                }

            }
        }
        // 到底终点所需步数
        step += 2;
        vis[beginx][beginy]=true;
        dfs(grid,beginx,beginy,1);
        return ret;
    }

    void dfs(vector<vector<int>>& grid,int i,int j,int count)
    {
        if(grid[i][j] == 2) 
        {
            if(count == step) //判断是否合法
                ++ret;
            return;
        }

        for(int k = 0; k < 4; ++k)
        {
            int x = i + dx[k], y = j + dy[k];
            if(x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] != -1)
            {
                vis[x][y]=true;
                dfs(grid,x,y,count+1);
                vis[x][y]=false;
            }
        }
    }
};
  • 52
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 73
    评论
评论 73
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值