Sword Pointer Offer 12 (Graph Search Part 1). The Path in the Matrix

Sword Pointer Offer 12 (Graph Search Part 1). The Path in the Matrix

Problem Description:

Given an mxn two-dimensional character grid board and a string word word. Returns true if word exists in the grid; otherwise, returns false.

Words must be formed alphabetically, through letters in adjacent cells, where "adjacent" cells are those that are horizontally or vertically adjacent. Letters in the same cell are not allowed to be used repeatedly.

image-20210825210750330

Example:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
输入:board = [["a","b"],["c","d"]], word = "abcd"
输出:false

Problem-solving ideas:Reference ideas

1. Backtracking algorithm plus depth-first solution

The backtracking algorithm is actually a search attempt process similar to enumeration, that is, to try one by one. We also try to solve this problem one by one. Let’s use Example 1 to draw a picture to see

image-20210825215630517

We see that he starts from a point in the rectangle to look up, down, left, and right. This point can be any point in the rectangle, so we should be able to write the general outline of the code, which is to traverse all the points of the rectangle. , and then start from this point to his 4 directions, because it is a two-dimensional array, so there are two for loops, the code is as follows:

 public boolean exist(char[][] board, String word) {
    
    
        //将字符串String word转变为char型数组
        char[] chars = word.toCharArray();
        //建立一个与矩阵(二维数组)同等长度的布尔型数组,用于探究矩阵数组中的元素是否使用
        boolean[][] isVisited = new boolean[board.length][board[0].length];
        //安全性检验
        if(board == null || board[0] == null || board.length == 0 || 
        board[0].length == 0 || word == null || word.equals("")){
    
    
            return false;
        }
        //遍历矩阵中的元素,找到与word数组中的第一个元素相同的元素
        for(int i = 0;i < board.length;i++){
    
    
            for(int j = 0;j < board[0].length;j++){
    
    
                if(board[i][j] == chars[0]){
    
    
                    //找到对应的元素时,通过广度优先搜索获取与前一个元素相邻元素,
                    //判断与word数组的下一个元素是否相同
                    if(dfs(board,i,j,isVisited,chars,0)){
    
    
                        return true;
                    }
                   
                }
            }
        }
        return false;
    }

The key code here is the function dfs, because we can search for each point in its 4 directions, so we can think of it as a 4-fork tree, that is, each node has 4 child nodes, and the traversal of the tree we The easiest thing to think of is recursion. Let's take a look at it:

 private boolean dfs(char[][] board,int i,int j,boolean[][] isVisited,char[] chars,int index){
    
    
 
        //安全性检验,最重要的是判断该元素是否已经被搜索
        if(i < 0 || j < 0 || i >= board.length || j >= board[0].length 
        || isVisited[i][j] || board[i][j] != chars[index]){
    
    
            return false;
        }

        //当在board元素中找到与word数组相对应的元素
        if(board[i][j] == chars[index]){
    
    
            //当元素索引为chars.length - 1时,代表已经搜索完word数组中的元素,返回true
            if(index == chars.length - 1){
    
    
                return true;
                }else{
    
    
                    //先将对应元素标记为已搜索
                    isVisited[i][j] = true;
                    //找到对应元素相邻元素
                    boolean res = dfs(board,i + 1,j,isVisited,chars,index + 1) 
                    ||dfs(board,i,j + 1,isVisited,chars,index + 1)
                    ||dfs(board,i - 1,j,isVisited,chars,index + 1) 
                    ||dfs(board,i,j - 1,isVisited,chars,index + 1);
                    //将对应元素的标记复原为未搜索
                    isVisited[i][j] = false;
                    return res;
                }
           
        }
        return false;

    }

How does backtracking solve this problem? To understand backtracking, we must first understand recursion. Recursion is divided into two steps, first recursion, and then return. When we pass down the current coordinates, we can modify the value of the current coordinates, and then restore the value of the current coordinates when returning to the current coordinates. This is the process of backtracking. Let's take a look at the code, which is much simpler than the above, and the operating efficiency will also be greatly improved.

Using backtracking plus depth-first search

class Solution {
    
    
    public boolean exist(char[][] board, String word) {
    
    
        //将字符串String word转变为char型数组
        char[] chars = word.toCharArray();
        //建立一个与矩阵(二维数组)同等长度的布尔型数组,用于探究矩阵数组中的元素是否使用
        boolean[][] isVisited = new boolean[board.length][board[0].length];
        //安全性检验
        if(board == null || board[0] == null || board.length == 0 || 
        board[0].length == 0 || word == null || word.equals("")){
    
    
            return false;
        }
        //遍历矩阵中的元素,找到与word数组中的第一个元素相同的元素
        for(int i = 0;i < board.length;i++){
    
    
            for(int j = 0;j < board[0].length;j++){
    
    
                if(board[i][j] == chars[0]){
    
    
                    //找到对应的元素时,通过广度优先搜索获取与前一个元素相邻元素,
                    //判断与word数组的下一个元素是否相同
                    if(dfs(board,i,j,isVisited,chars,0)){
    
    
                        return true;
                    }
                   
                }
            }
        }
        return false;
    }
    //建立广度优先搜索方法
    private boolean dfs(char[][] board,int i,int j,boolean[][] isVisited,char[] chars,int index){
    
    
 
        //安全性检验,最重要的是判断该元素是否已经被搜索
        if(i < 0 || j < 0 || i >= board.length || j >= board[0].length 
        || isVisited[i][j] || board[i][j] != chars[index]){
    
    
            return false;
        }

        //当在board元素中找到与word数组相对应的元素
        if(board[i][j] == chars[index]){
    
    
            //当元素索引为chars.length - 1时,代表已经搜索完word数组中的元素,返回true
            if(index == chars.length - 1){
    
    
                return true;
                }else{
    
    
                    //先将对应元素标记为已搜索
                    isVisited[i][j] = true;
                    //找到对应元素相邻元素
                    boolean res = dfs(board,i + 1,j,isVisited,chars,index + 1) 
                    ||dfs(board,i,j + 1,isVisited,chars,index + 1)
                    ||dfs(board,i - 1,j,isVisited,chars,index + 1) 
                    ||dfs(board,i,j - 1,isVisited,chars,index + 1);
                    //将对应元素的标记复原为未搜索
                    isVisited[i][j] = false;
                    return res;
                }
           
        }
        return false;

    }
}

Problem-solving idea 2:reference link

This problem is a typical matrix search problem, which can be solved using depth-first search (DFS) + pruning.

  • Depth-first search: It can be understood as a brute force method to traverse all string possibilities in the matrix. DFS uses recursion to search to the end in one direction, then backtrack to the previous node, search in another direction, and so on.
  • Pruning: In the search, if this path is impossible to match the target string successfully (for example: this matrix element is different from the target character, this element has been accessed), it should be returned immediately, which is called feasibility pruning.

image-20210826134103920

DFS resolution:

  • Recursive parameters: the row and column indexes i and j of the current element in the matrix board, and the index k of the current target character in word.
  • Termination Condition: Returns false: (1) row or column index is out of range or (2) current matrix element is different from target character or (3) current matrix element has been visited ((3) can be merged into (2)). Return truetrue: k = len(word) - 1, that is, the string word has all been matched.
  • Recursive work:
  1. Mark the current matrix element: Change board[i] [j] to an empty character '', which means that this element has been visited to prevent repeated visits in subsequent searches. (The empty character is represented as '\0' in java)
  2. Search the next cell: Open the lower layer recursion towards the current element's up, down, left, and right directions, use or connect (representing that you only need to find a feasible path and return directly, and do not do subsequent DFS), and record the result to res.
  3. Restore the current matrix elements: Restore the board[i] [j] elements to their initial values, word[k].
  4. Return value: Returns the boolean res, representing whether the target string is found.
class Solution {
    
    
    public boolean exist(char[][] board, String word) {
    
    
        char[] words = word.toCharArray();
        if(board == null || board.length == 0 || board[0].length == 0 
        || words == null || words.length == 0){
    
    
            return false;
        }
        int index = 0;
        for(int i = 0 ;i < board.length;i++){
    
    
            for(int j = 0;j < board[0].length;j++){
    
    
                if(board[i][j] == words[0]){
    
    
                    if(dfs(board,i,j,words,index)){
    
    
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private boolean dfs(char[][] board,int i,int j,char[] words,int index){
    
    
        //安全性检验,board[i][j] != words[index]
        if(i < 0 || j < 0 || i >= board.length || j >= board[0].length || board[i][j] != words[index]){
    
    
            return false;
        }
        if(index == words.length - 1){
    
    
            return true;
        }else{
    
    
             //将对应矩阵中的元素标记为空
            board[i][j] = '\0';
            boolean res = dfs(board,i + 1,j,words,index + 1) 
            ||dfs(board,i,j + 1,words,index + 1)
            ||dfs(board,i - 1,j,words,index + 1) 
            ||dfs(board,i,j - 1,words,index + 1);
            //将board[i][j]对应元素复原
            board[i][j] = words[index];
            return res;
        }
       
        
    }
}

Guess you like

Origin blog.csdn.net/Royalic/article/details/119930235