Leetcode - Word Search

My code:

import java.util.HashSet;

public class Solution {
    private int width = 0;
    private int height = 0;
    public boolean exist(char[][] board, String word) {
        if (board == null)
            return false;
        if (board.length == 0)
            return false;
        if (word == null || word.length() == 0)
            return false;
        width = board[0].length;
        height = board.length;
        HashSet<Integer> isVisited = new HashSet<Integer>();
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                boolean ret = helper(i, j, 0, board, word, isVisited);
                if (ret)
                    return true;
            }
        }
        return false;
    }
    
    private boolean helper(int i, int j, int begin, char[][] board, String word, HashSet<Integer> isVisited) {
        if (!isValid(i, j, isVisited))
            return false;
        if (board[i][j] != word.charAt(begin))
            return false;
        if (begin == word.length() - 1)
            return true;
        isVisited.add(i * width + j);
        if (helper(i - 1, j, begin + 1, board, word, isVisited))
            return true;
        if (helper(i + 1, j, begin + 1, board, word, isVisited))
            return true;
        if (helper(i, j - 1, begin + 1, board, word, isVisited))
            return true;
        if (helper(i, j + 1, begin + 1, board, word, isVisited))
            return true;
        isVisited.remove(i * width + j);
        return false;
    }
    
    /**
     * detect whether this location in array is valid. If it is visited, it is invalid
     * @param i row
     * @param j col
     * @param isVisited boolean[][]
     * @return whether it is valid
     */
    private boolean isValid(int i, int j, HashSet<Integer> isVisited) {
        if (i < 0 || i >= height)
            return false;
        if (j < 0 || j >= width)
            return false;
        return !isVisited.contains(i * width + j);
    }
    
    public static void main(String[] args) {
        char[][] board = new char[3][4];
        board[0][0] = 'A';
        board[0][1] = 'B';
        board[0][2] = 'C';
        board[0][3] = 'E';
        
        board[1][0] = 'S';
        board[1][1] = 'F';
        board[1][2] = 'C';
        board[1][3] = 'S';
        
        board[2][0] = 'A';
        board[2][1] = 'D';
        board[2][2] = 'E';
        board[2][3] = 'E';
        
        Solution test = new Solution();
        System.out.println(test.exist(board, "ADFA"));
    }
}

總算是過了測試。一開始我是用一個(gè),
boolean[][] isVisited 數(shù)組來做的。然后每次遞歸,為了避免重復(fù)訪問同一個(gè)元素,我都需要把原布爾數(shù)組拷貝一份傳入下層遞歸。四個(gè)方向,上下左右,都需要拷貝一下原數(shù)組。當(dāng)原數(shù)組無比巨大時(shí),而我的每一次遞歸又都需要拷貝四次原數(shù)組,導(dǎo)致我的時(shí)間測試過不了。我在八皇后問題中就是用這個(gè)辦法做的,現(xiàn)在看來不是很好,或者說,很糟糕。因?yàn)閿?shù)組只是很少的一部分發(fā)生了改變,但我需要重新拷貝整個(gè)數(shù)組。很像是 meng project 中的bitmap, 浪費(fèi)了大量的內(nèi)存。
然后我換用了一個(gè)HashSet<Integer>, 每次把對應(yīng)位置的i,j轉(zhuǎn)換為一個(gè)特定的index存入hash set。
之后再刪除。避免了布爾數(shù)組的問題并且可以達(dá)到相同的效果。
于是,過了測試。但是時(shí)間仍然很高。
然后我看了答案。
發(fā)現(xiàn),我的HashSet在遞歸前插入元素,遞歸后刪除元素的做法可以再簡化。
遞歸前,設(shè)置該元素為board[i][j] = '#', 因?yàn)?#不是字母,所以以后的深層遞歸是無法訪問到他的。
遞歸后,再把他改回來,不要影響其他循環(huán)下的遞歸。

代碼如下:

public class Solution {
    private int width = 0;
    private int height = 0;
    public boolean exist(char[][] board, String word) {
        if (board == null)
            return false;
        if (board.length == 0)
            return false;
        if (word == null || word.length() == 0)
            return false;
        width = board[0].length;
        height = board.length;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                boolean ret = helper(i, j, 0, board, word);
                if (ret)
                    return true;
            }
        }
        return false;
    }
    
    private boolean helper(int i, int j, int begin, char[][] board, String word) {
        if (i < 0 || i >= height || j < 0 || j >= width)
            return false;
        if (board[i][j] != word.charAt(begin))
            return false;
        char temp = board[i][j];
        board[i][j] = '#';
        if (begin == word.length() - 1)
            return true;
        if (helper(i - 1, j, begin + 1, board, word))
            return true;
        if (helper(i + 1, j, begin + 1, board, word))
            return true;
        if (helper(i, j - 1, begin + 1, board, word))
            return true;
        if (helper(i, j + 1, begin + 1, board, word))
            return true;
        board[i][j] = temp;
        return false;
    }
    
    public static void main(String[] args) {
        char[][] board = new char[3][4];
        board[0][0] = 'A';
        board[0][1] = 'B';
        board[0][2] = 'C';
        board[0][3] = 'E';
        
        board[1][0] = 'S';
        board[1][1] = 'F';
        board[1][2] = 'C';
        board[1][3] = 'S';
        
        board[2][0] = 'A';
        board[2][1] = 'D';
        board[2][2] = 'E';
        board[2][3] = 'E';
        
        Solution test = new Solution();
        System.out.println(test.exist(board, "ADFA"));
    }
}

http://www.programcreek.com/2014/06/leetcode-word-search-java/

思想其實(shí)一直沒變過。就是dfs, 不停地往深層遞歸。就是八皇后問題。
這類問題的統(tǒng)一思想就是,
知道最終的目的是什么,確定停止條件。即什么時(shí)候返回true,不再往下遞歸。
然后,每次遞歸的時(shí)候,往上下左右各個(gè)方向擴(kuò)散,并且判斷這些擴(kuò)散的方向是否有效。他們是否越界了?
他們是否之前已經(jīng)被訪問過了。
如果判斷之前是否被訪問過了,最簡單的做法就是布爾數(shù)組,然后再每次深層遞歸時(shí)重新拷貝一份傳進(jìn)去。
但完全可以用HashSet<Integer> 來代替,免去了拷貝的過程,節(jié)約了大量的時(shí)間。
然后這道題目告訴我們還可以再簡化。
將該元素設(shè)置為一個(gè)不滿足再次深層遞歸條件的元素。這樣,深層遞歸時(shí)再次訪問到該元素時(shí)會自動停止。這樣就避免了HashSet操作所帶來的消耗。遞歸結(jié)束后,再將該元素恢復(fù)到原來的值,避免影響其他循環(huán)中的遞歸。

for (int i = 0; i < height; i++)
    for (int j = 0; j < width; j++)
           helper(...);  // recursive function

**
Anyway, Good luck, Richardo!
**

My code:

public class Solution {
    int row = 0;
    int col = 0;
    public boolean exist(char[][] board, String word) {
        if (board == null || board.length == 0 || board[0].length == 0 || word == null || word.length() == 0) {
            return false;
        }
        
        row = board.length;
        col = board[0].length;
        boolean[][] isVisited = new boolean[row][col];
        
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                isVisited[i][j] = true;
                boolean flag = helper(0, word, i, j, board, isVisited);
                isVisited[i][j] = false;
                if (flag) {
                    return true;
                }
            }
        }
        
        return false;
    }
    
    private boolean helper(int position, String word, int i, int j, char[][] board, boolean[][] isVisited) {
        if (position == word.length() - 1) {
            char curr = word.charAt(position);
            if (curr == board[i][j]) {
                return true;
            }
            else {
                return false;
            }
        }
        else {
            char curr = word.charAt(position);
            if (curr == board[i][j]) {
                if (isValid(i + 1, j) && !isVisited[i + 1][j]) {
                    isVisited[i + 1][j] = true;
                    boolean flag = helper(position + 1, word, i + 1, j, board, isVisited);
                    isVisited[i + 1][j] = false;
                    if (flag)
                        return flag;
                }
                if (isValid(i - 1, j) && !isVisited[i - 1][j]) {
                    isVisited[i - 1][j] = true;
                    boolean flag = helper(position + 1, word, i - 1, j, board, isVisited);
                    isVisited[i - 1][j] = false;
                    if (flag)
                        return flag;
                }
                if (isValid(i, j + 1) && !isVisited[i][j + 1]) {
                    isVisited[i][j + 1] = true;
                    boolean flag = helper(position + 1, word, i, j + 1, board, isVisited);
                    isVisited[i][j + 1] = false;
                    if (flag)
                        return flag;
                }
                if (isValid(i, j - 1) && !isVisited[i][j - 1]) {
                    isVisited[i][j - 1] = true;
                    boolean flag = helper(position + 1, word, i, j - 1, board, isVisited);
                    isVisited[i][j - 1] = false;
                    if (flag)
                        return flag;
                }
                return false;
            }
            else {
                return false;
            }
        }
    }
    
    private boolean isValid(int i, int j) {
        if (i >= 0 && i < row && j >= 0 && j < col) {
            return true;
        }
        else {
            return false;
        }
    }
    
}

這道題目就是dfs,體力活。沒什么難度。

Anyway, Good luck, Richardo! -- 09/02/2016

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,362評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,258評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,234評論 2 375

推薦閱讀更多精彩內(nèi)容