1. 程式人生 > >leetcode 第79題:BFS(廣度優先搜尋)的簡單應用

leetcode 第79題:BFS(廣度優先搜尋)的簡單應用

題目大意:

  給出一個二維陣列,再給出一個字串,試問該字串是否存在於這個二維陣列中?

題目說明:

  所謂“字串存在於二維陣列中”,即從二維陣列的任一元素出發,從上下左右四個方向“前進”,直到找到整條字串。如下:
  輸入:[[‘A’,’B’,’C’,’E’], [‘S’,’F’,’C’,’S’], [‘A’,’D’,’E’,’E’]],構成的二維陣列可以寫成矩陣形式:
這裡寫圖片描述
  如果此時給定的字串是”ABCCED”或”SEE”,則應當輸出”true”。若給定字串是”ABCB”,應當輸出”false”,因為在這個二維陣列中找不到連續的’A’、’B’、’C’和’B’組成的字串。
  要說明的是,二維陣列中同一位置的字元不能重複使用兩次。

解題思路

  剛看到這道題,就能明確這是一道用BFS解決的題目。解題思路也很清晰:遍歷二維陣列,從當前點出發,比較所在點的字元與給定字串當前位置的字元是否相等,若相等,則向四個方向前進,並把字串的“當前位置”向後推移一位,直到達到字串末尾。具體操作起來,在“前進”過程中,維護合法字串長的最大值,一旦它與給定字串的長度相等,則說明存在。
  為保證同一個字元不會被使用兩次,用陣列flag記錄每一個位置上字元的使用情況。具體實現如下:

class Solution {
public:
    int row,column;
    int flag[1000][1000];
    int comp(int
a,int b,int c,int d) { int k; k=(a>=b?a:b); k=(k>=c?k:c); k=(k>=d?k:d); return k; } int check(int i,int j, int seq, vector< vector<char> >& board, string word) { if(i>=0&&i<row&&j>=0&&j<column&&flag[i][j]==0
&&seq<word.size()) //未超邊界,未訪問過且字元相等 { char c; c=board[i][j]; //取i,j處的值 if(c==word.at(seq)) { flag[i][j]=1; return comp(check(i+1,j,seq+1,board,word)+1,check(i,j+1,seq+1,board,word)+1,check(i-1,j,seq+1,board,word)+1,check(i,j-1,seq+1,board,word)+1); } else return 0; } else return 0; } bool exist(vector<vector<char>>& board, string word) { int number,num; column=board[0].size(); //列數 row=board.size(); //行數 num=word.size(); for(int i=0;i<row;i++) //逐個遍歷 for(int j=0;j<column;j++) { memset(flag,0,sizeof(flag)); //將標誌陣列初始化 number=check(i,j,0,board,word); if(number==num) return true; } return false; } };

  不幸的是,上傳程式碼後只得到了WA。仔細檢查,發現問題就出在flag陣列的更新上。以如下的二維陣列為例:
   這裡寫圖片描述
  給定字串是”ABCESEEEFS”。可以發現,在程式按A(1,1)-B-C-E(2,3)-S-E(3,4)-E(3,3)的路線走後,發現再也沒有與給定字串匹配的路線。此時,由於(2,3)和(3,3)處的flag值被設為1,程式再也返回不到(1,3)處的C,從而使得結果輸出錯誤。
  要解決這一問題,必須在“此路不通”的情況下,修改flag的值,使得程式能返回到“岔路口”。參考了discuss中的結果,修改程式如下:

class Solution {
public:
    int row,column;
    int flag[1000][1000];
    bool check(int i,int j, int seq, vector< vector<char> >& board, string word)
    {
        if(i<0||i>=row||j<0||j>=column||flag[i][j]==1||board[i][j]!=word.at(seq))  //超邊界,訪問過或者不相等
            return false;
        char c;
        if(seq==word.size()-1) return true;  //如果是最後一個字元,則直接返回true
        c=board[i][j];  //否則,取(i,j)處的值
        flag[i][j]=1;
        if(check(i+1,j,seq+1,board,word)||check(i,j+1,seq+1,board,word)||check(i-1,j,seq+1,board,word)||check(i,j-1,seq+1,board,word))  //只要有一項成立即可,否則,需將標誌改回來,再返回false,表示此路不通
            return true;
        flag[i][j]=0;   
        return false;
    }
    bool exist(vector<vector<char>>& board, string word) {
    bool jud;
    column=board[0].size();  //列數
    row=board.size();  //行數
    for(int i=0;i<row;i++)  //逐個遍歷
    for(int j=0;j<column;j++)
    {
        memset(flag,0,sizeof(flag));  //將標誌陣列初始化
        jud=check(i,j,0,board,word);
        if(jud) return true;
    }
    return false;
    }
};

  這次不用長度作為判斷是否存在標誌,而是直接將搜尋結果返回。但執行的結果卻是超時。仔細對比了修改的程式與discuss中給出的程式,發現差別主要在於:自己寫的程式中用到了新建的陣列flag,每遍歷一個位置的字元時就要將其重新初始化為0,而discuss中的程式則是將修改flag值的操作改成修改board陣列相應位置元素為’/0’的操作,這樣就少了將flag整體賦為0的步驟,整體降低了時間複雜度。
  需要說明的是,雖然引用傳遞會改變實參(本問題中即原二維陣列board)的值,但在程式中已經做了相應的處理:對每條走不通路徑上的值都將其還原。這樣,就不用擔心每遍歷一次,原陣列的值修改一次的問題了。程式碼如下:

   class Solution {
    public:
         bool exist(vector<vector<char> > &board, string word) {
             m=board.size();
             n=board[0].size();
            for(int x=0;x<m;x++)
                for(int y=0;y<n;y++)
                {
                    if(isFound(board,word.c_str(),x,y))
                        return true;
                }
            return false;
        }
    private:
        int m;
        int n;
        bool isFound(vector<vector<char> > &board, const char* w, int x, int y)
        {
            if(x<0||y<0||x>=m||y>=n||board[x][y]=='\0'||*w!=board[x][y])
                return false;
            if(*(w+1)=='\0')
                return true;
            char t=board[x][y];
            board[x][y]='\0';
            if(isFound(board,w+1,x-1,y)||isFound(board,w+1,x+1,y)||isFound(board,w+1,x,y-1)||isFound(board,w+1,x,y+1))
                return true; 
            board[x][y]=t;  //將值還原
            return false;
        }
    };

  這道題目雖然只是BFS的一道簡單應用題,但背後需要注意的細節其實很多。這提醒我在編寫程式的時候一定要提前把所有要注意到的方面想清楚,這樣才能事半功倍。

相關推薦

leetcode 79BFS(廣度優先搜尋)的簡單應用

題目大意:   給出一個二維陣列,再給出一個字串,試問該字串是否存在於這個二維陣列中? 題目說明:   所謂“字串存在於二維陣列中”,即從二維陣列的任一元素出發,從上下左右四個方向“前進”,直到找到整條字串。如下:   輸入:[[‘A’,’B’,’C

LeetCode——198驗證二叉搜尋

題目: 給定一個二叉樹,判斷其是否是一個有效的二叉搜尋樹。 假設一個二叉搜尋樹具有如下特徵: 節點的左子樹只包含小於當前節點的數。 節點的右子樹只包含大於當前節點的數。 所有左子樹和右子樹自身必須也是二叉搜尋樹。 示例 1: 輸入:

BFS(廣度優先搜尋)簡單例題(二)

本篇部落格是關於BFS(廣度優先搜尋)問題的練習。 Battle City     POJ   2312 #include<stdio.h> #include<iostream> #include<string.h> #includ

leetcode32最長有效括號(遇到一個奇葩的錯誤)

問題描述: 給一個只包含 '(' 和 ')' 的字串,找出最長的有效(正確關閉)括號子串的長度。 對於 "(()",最長有效括號子串為 "()" ,它的長度是 2。 另一個例子 ")()())",最長有效括號子

leetcode72編輯距離

給定兩個單詞 word1 和 word2,計算出將 word1 轉換成 word2 所使用的最少運算元 。 你可以對一個單詞進行如下三種操作: 插入一個字元 刪除一個字元 替換一個字元 示例 1:

leetcode兩個有序陣列的中位數

給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2。 請你找出這兩個有序陣列的中位數,並且要求演算法的時間複雜度為 O(log(m + n))。 你可以假設 nums1 和 nums2 不會同時為空。 示例

leetcode11盛水最多的容器

給定 n 個非負整數 a1,a2,...,an,每個數代表座標中的一個點 (i, ai) 。在座標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0)。找出其中的兩條線,

LeetCode23合併K個有序連結串列(JAVA實現)

題目: 我的解答: 思路很簡單,把所有的資料先讀到ArrayList中然後轉到陣列中,然後排序,然後構建新連結串列 程式碼: /** * Definition for singly-linked list. * public class ListNode {

leetcode26刪除排序陣列的重複項

給定一個排序陣列,你需要在原地刪除重複出現的元素,使得每個元素只出現一次,返回移除後陣列的新長度。 不要使用額外的陣列空間,你必須在原地修改輸入陣列並在使用 O(1) 額外空間的條件下完成。 給定陣列 nums = [1,1,2], 函式應該返回新的長度 2, 並且原陣列 nums 的前兩個元素被修改

leetcode26刪除排序數組的重復項

刪除重復 duplicate 新的 http hit 出現一次 返回 -h color 給定一個排序數組,你需要在原地刪除重復出現的元素,使得每個元素只出現一次,返回移除後數組的新長度。 不要使用額外的數組空間,你必須在原地修改輸入數組並在使用 O(1) 額外空間的條件下完

LeetCode24兩兩交換連結串列的節點(JAVA實現)

題目: 我的解答: /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(i

LeetCode最長迴文子串(C語言)

給定一個字串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度為1000。 示例 1: 輸入: “babad” 輸出: “bab” 注意: "aba"也是一個有效答案。 示例 2: 輸入: “cbbd” 輸出: “bb” 解法一:暴力求解法 思想:

leetcode39組合綜合

ret sum solver append else end 包含 color 排序 給定一個無重復元素的數組 candidates 和一個目標數 target ,找出 candidates 中所有可以使數字和為 target 的組合。 candidates 中的數字可以無

LeetCode35搜尋插入位置

題目描述: 給定一個排序陣列和一個目標值,在陣列中找到目標值,並返回其索引。如果目標值不存在於陣列中,返回它將會被按順序插入的位置。 你可以假設陣列中無重複元素。 示例 1: 輸入: [1,3,5,6], 5 輸出: 2 示例 2: 輸入: [1,3,5,6], 2 輸出: 1 示例 3: 輸入: [

LeetCode7Reverse Integer

題目描述:反轉整數輸出,注意的是輸入被假定為一個32位有符號整數。當反整數溢位時,函式應返回0。   今年的華為的實習生招聘筆試的第一個題與這個有點類似,不過華為的是輸入兩個整數,反轉,然後輸出兩個

LeetCodeLeetCode——7Reverse Integer

7. Reverse Integer    My Submissions Question Editorial Solution Total Accepted: 134949 Tota

LeetCode——36有效的數獨

時間:27ms 題目: 判斷一個 9x9 的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。 數字 1-9 在每一行只能出現一次。 數字 1-9 在每一列只能出現一次。 數字 1-9 在每一個以粗實線分隔的 3x3 宮內只能出現一次。

LeetCodeLeetCode——2Add Two Numbers

You are given two linked lists representing two non-negative numbers. The digits are stored in reverse order and each of their nodes contain a single dig

leetcodeReverse Integer

題目: Given a 32-bit signed integer, reverse digits of an integer. Example 1: Input: 123 Output: 321 Example 2: Input: -123 Ou

Leetcode83刪除排序連結串列中的重複元素

給定一個排序連結串列,刪除所有重複的元素使得每個元素只留下一個。案例:給定 1->1->2,返回 1->2給定 1->1->2->3->3,返回 1->2->3解題思路:使用2個指標,一個p,一個q; q = p.next