LeetCode算法题5.1:DFS/BFS - 单词搜索


单词搜索

      题目链接:https://leetcode-cn.com/problems/word-search/

      题目描述:
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

DFS :

      算法1:对每一个可能的点采用DFS遍历,判断是否可以得到唯一解,若存在即返回true,否则为 false。其中在递归过程中,flag 数组的值需要在递归之前置为 true,递归之后置为 flase,即它的状态需要回退。参考代码如下(带注释):

	int[] addi={1,-1,0,0};
    int[] addj={0,0,1,-1};
    boolean[][] flag;
    public boolean exist(char[][] board, String word) {
        int m=board.length,n=board[0].length;
        flag=new boolean[m][n];//flag数组用来标记某个字符是否被访问过
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(board[i][j]==word.charAt(0)){//对每一个首字符都需要进行DFS遍历,判断是否在此处可以搜索到到完整的字符串
                    if(solve(board,i,j,word,1))
                        return true;
                }
            }
        }
        return false;
    }
    /*想一想,在每次调用 slove 方法时,flag 数组的所有值应该都为 false 吧,即上一次 solve 方法对 flag 造成的
    影响和下一次 solve 方法调用 flag 无关, flag 的功能仅是为了在一次遍历中不要重复访问已访问过的字符。
    一次不成功的搜索之后,访问过的元素(flag 值为 true)应该需要再次置为 false。在下一次搜索时应该还可以访问
    这些元素,也就是说,flag 数组的值有一个回退的动作,*/
    
    boolean solve(char[][] board,int i,int j,String word,int count){
        if(count==word.length())//退出条件
            return true;
        flag[i][j]=true; //flag 中的当前元素以访问,置为 true
        boolean re=false;//保存退出返回值
        for(int k=0;k<4;k++){
            int newI=i+addi[k],newJ=j+addj[k];
            if(newI<0||newJ<0||newI>=board.length||newJ>=board[0].length||flag[newI][newJ]==true)
                continue;
            if(board[newI][newJ]==word.charAt(count))//一种可能成功的情况
                re=re||solve(board,newI,newJ,word,count+1);//这里为或操作
        }
        flag[i][j]=false; //flag 重新恢复为false
        return re;
    }

      算法2:另外一种描述方法如下(添加一个 boolean 变量 ans 来判断是否搜索到字符串):

int[] addi={1,-1,0,0};
    int[] addj={0,0,1,-1};
    boolean[][] flag;
    boolean ans;
    public boolean exist(char[][] board, String word) {
        int m=board.length,n=board[0].length;
        flag=new boolean[m][n];
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(board[i][j]==word.charAt(0)){
                    solve(board,i,j,word,1);
                    if(ans==true)
                        return true;
                }
            }
        }
        return false;
    }
    void solve(char[][] board,int i,int j,String word,int count){
        if(count==word.length()){
            ans=true;
            return;
        }
        flag[i][j]=true;
        for(int k=0;k<4;k++){
            int newI=i+addi[k],newJ=j+addj[k];
            if(newI<0||newJ<0||newI>=board.length||newJ>=board[0].length||flag[newI][newJ]==true)
                continue;
            if(board[newI][newJ]==word.charAt(count))
                solve(board,newI,newJ,word,count+1);
        }
        flag[i][j]=false;
    }

小小的优化

      算法1 和 2 都还需要优化,它们都有同一个问题,在搜索到字符串之后不会立即返回,这样会浪费时间的。算法1 改进如下:

	for(int k=0;k<4;k++){
            int newI=i+addi[k],newJ=j+addj[k];
            if(newI<0||newJ<0||newI>=board.length||newJ>=board[0].length||flag[newI][newJ]==true)
                continue;
            if(board[newI][newJ]==word.charAt(count)){
                re=re||solve(board,newI,newJ,word,count+1);
                if(re)
                    break;
            }

    }

      一旦搜索到字符串之后,此时re 为 true,短路 || 不会执行 solve(board,newI,newJ,word,count+1) ,但是也应该加上 break 语句让程序执行跳出 for 循环,直接退出返回正确的结果。相应的也可对算法 2 进行优化。


# 总结       深度优先遍历是回溯算法使用的策略。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值