原題
給定一個二維網格和一個單詞,找出該單詞是否存在於網格中。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
總結
以上就是這道題目個人解答過程了,不知道你們是否理解了。這道題主要就是回溯
,針對邊界狀況須要注意,應該就沒有其餘問題了。
有興趣的話能夠訪問個人博客或者關注個人公衆號、頭條號,說不定會有意外的驚喜。
公衆號:健程之道