力扣79——單詞搜索

原題

給定一個二維網格和一個單詞,找出該單詞是否存在於網格中。git

單詞必須按照字母順序,經過相鄰的單元格內的字母構成,其中「相鄰」單元格是那些水平相鄰或垂直相鄰的單元格。同一個單元格內的字母不容許被重複使用。github

示例:數組

board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

給定 word = "ABCCED", 返回 true.
給定 word = "SEE", 返回 true.
給定 word = "ABCB", 返回 false.

原題url:https://leetcode-cn.com/problems/word-search/優化

解題

回溯

拿到這題,我一開始想到的方法就是:this

  • 以每一格爲起點,開始尋找,尋找的條件是要保證當前的字母和下一個和它鏈接的字母(上下左右)都符合條件,那麼就繼續查找。
  • 只要當前不符合,馬上返回 false,快速失敗。
  • 利用一個二維 boolean 數組記錄每一格的使用狀況,記住,若是從當前格出發都不成功的話,則須要回退

接下來看看代碼:url

class Solution {
    // 總行數
    int row;
    // 總列數
    int col;
    // 原數組
    char[][] board;
    // 須要尋找的字符數組
    char[] wordArray;

    public boolean exist(char[][] board, String word) {
        this.board = board;
        this.row = board.length;
        this.col = board[0].length;
        this.wordArray = word.toCharArray();
				// 標記每一格是否用過的二維數組
        boolean[][] used = new boolean[row][col];
				
				// 以每一格爲起點開始搜索
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (dfs(i, j, 0, used)) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean dfs(int x, int y, int index, boolean[][] used) {
        // 當前位置是否符合條件
        if (board[x][y] != wordArray[index]) {
            return false;
        }
        // 所有找完了
        if (index == wordArray.length - 1) {
            return true;
        }
        // 設置當前格使用過了
        used[x][y] = true;

        // 尋找上下左右是否有符合下一個的狀況
        
        // 上一格是否存在而且沒有被使用過
        if (x > 0 && !used[x - 1][y]) {
            if (dfs(x - 1, y, index + 1, used)) {
                return true;
            }
        }

        // 下一格是否存在而且沒有被使用過
        if (x < row - 1 && !used[x + 1][y]) {
            if (dfs(x + 1, y, index + 1, used)) {
                return true;
            }
        }

        // 左一格是否存在而且沒有被使用過
        if (y > 0 && !used[x][y - 1]) {
            if (dfs(x, y - 1, index + 1, used)) {
                return true;
            }
        }

        // 右一格是否存在而且沒有被使用過
        if (y < col - 1 && !used[x][y + 1]) {
            if (dfs(x, y + 1, index + 1, used)) {
                return true;
            }
        }

        // 上下左右的狀況都走完了,所以回退,設置當前格沒有使用過
        used[x][y] = false;

        return false;
    }
}

提交OK,執行用時: 19 ms,內存消耗:38.3 MB。從時間上看起來還有很多優化的空間,那該怎麼作呢?code

彷佛無用的優化

我看了別人更優的解法,發現思想都是一致的,只是在判斷上可能會更加簡潔一些,若是是判斷快速失敗的話,彷佛沒有什麼本質上的區別。我將本身的寫法稍微優化了一下:內存

class Solution {
    // 總行數
    int row;
    // 總列數
    int col;
    // 原數組
    char[][] board;
    // 須要尋找的字符數組
    char[] wordArray;

    public boolean exist(char[][] board, String word) {
        this.board = board;
        this.row = board.length;
        this.col = board[0].length;
        this.wordArray = word.toCharArray();
        // 標記每一格是否用過的二維數組
        boolean[][] used = new boolean[row][col];
				
        // 以每一格爲起點開始搜索
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (dfs(i, j, 0, used)) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean dfs(int x, int y, int index, boolean[][] used) {
        // 當前位置不存在或者使用過,則返回失敗
        if (x < 0 || x >= row || y < 0 || y >= col || used[x][y]) {
            return false;
        }

        // 當前位置是否符合條件
        if (board[x][y] != wordArray[index]) {
            return false;
        }
        // 所有找完了
        if (index == wordArray.length - 1) {
            return true;
        }
        // 設置當前格使用過了
        used[x][y] = true;

        // 尋找上下左右是否有符合下一個的狀況
        boolean flag = dfs(x - 1, y, index + 1, used) || dfs(x + 1, y, index + 1, used) ||
                        dfs(x, y - 1, index + 1, used) || dfs(x, y + 1, index + 1, used);

        // 上下左右的狀況都走完了,所以回退,設置當前格沒有使用過
        used[x][y] = false;

        return flag;
    }
}

提交OK,執行用時: 5 ms,內存消耗:38.4 MB。用時上少了不少,應該在於判斷上:leetcode

  • 針對位置是否存在的判斷,以前的寫法是判斷下一個位置是否存在,分散在四個 if 判斷中,如今是寫在一個裏面,用於判斷當前位置。
  • 尋找上下左右時,由於邏輯運算||是支持短路的,因此和以前分在四個 if 中效果是差很少的,但看起來更加簡潔。

好吧,其實我本身也沒有看懂爲何這樣寫時間上會減小,你們若是知道的話,歡迎在下方留言。get

總結

以上就是這道題目個人解答過程了,不知道你們是否理解了。這道題主要就是回溯,針對邊界狀況須要注意,應該就沒有其餘問題了。

有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。

https://death00.github.io/

公衆號:健程之道

相關文章
相關標籤/搜索