搜索---廣度優先

廣度優先搜索一層一層地進行遍歷,每層遍歷都以上一層遍歷的結果作爲起點,遍歷一個距離能訪問到的所有節點。需要注意的是,遍歷過的節點不能再次被遍歷。
每一層遍歷的節點都與根節點距離相同。設 di 表示第 i 個節點與根節點的距離,推導出一個結論:對於先遍歷的節點 i 與後遍歷的節點 j,有 di <= dj。利用這個結論,可以求解最短路徑等 最優解 問題:第一次遍歷到目的節點,其所經過的路徑爲最短路徑。應該注意的是,使用 BFS 只能求解無權圖的最短路徑,無權圖是指從一個節點到另一個節點的代價都記爲 1。

在程序實現 BFS 時需要考慮以下問題:

隊列:用來存儲每一輪遍歷得到的節點;
標記:對於遍歷過的節點,應該將它標記,防止重複遍歷。

1.計算在網格中從原點到特定點的最短路徑長度

/*
    * 計算在網格中從原點到特定點的最短路徑長度
    * 在一個 N × N 的方形網格中,每個單元格有兩種狀態:空(0)或者阻塞(1)。
    * */
    public int shortestPathBinaryMatrix(int[][] grid) {
        int direction[][] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
        int n = grid.length;
        Queue<Pair<Integer, Integer>> queue = new LinkedList<>();
        if (grid[0][0] == 0) {
            if (n == 1) return 1;
            queue.add(new Pair<>(0, 0));
        }
        int path = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();
            path++;
            while (size-- != 0) {//把每層的節點遍歷完
                Pair<Integer, Integer> curElem = queue.poll();
                int x = curElem.getKey();
                int y = curElem.getValue();
                for (int i = 0; i < 8; i++) {
                    int curx = direction[i][0] + x, cury = direction[i][1] + y;
                    if (curx < 0 || curx >= n || cury < 0 || cury >= n || grid[curx][cury] == 1) continue;
                    if (curx == n - 1 && cury == n - 1) return path + 1;
                    queue.add(new Pair<>(curx, cury));
                    grid[curx][cury] = 1;//標記一下已走過
                }
            }
        }
        return -1;
    }

2. 組成整數的最小平方數數量

    /*
    * 題目:完全平方數
    * 描述:給定正整數 n,找到若干個完全平方數(比如 1, 4, 9, 16, ...)使得它們的和等於 n。
    * 你需要讓組成和的完全平方數的個數最少。
    * 分析:
    * 可以將每個整數看成圖中的一個節點,如果兩個整數之差爲一個平方數,那麼這兩個整數所在的節點就有一條邊。
    * 要求解最小的平方數數量,就是求解從節點 n 到節點 0 的最短路徑。
    * 本題也可以用動態規劃求解
    * */
    public int numSquares(int n) {
        if (n == 0) return 0;
        List<Integer> squares = squareWithinN(n);
        Queue<Integer> queue = new LinkedList<>();
        queue.add(n);
        int book[] = new int[n];
        int path = 0;
        while (!queue.isEmpty()) {
            int size = queue.size();
            path++;
            while (size-- != 0) {
                int cur = queue.poll();
                for (Integer square : squares) {
                    int newNode = cur - square;
                    if (newNode < 0) break;
                    if (newNode == 0) return path;
                    if (book[newNode] == 1) continue;
                    queue.add(newNode);
                    book[newNode] = 1;
                }
            }
        }
        return -1;
    }

    //求n以內的平方數
    public List<Integer> squareWithinN(int n) {
        List<Integer> squares = new LinkedList<>();
        for (int a = 1; a <= Math.sqrt(n); a++) {
            squares.add(a * a);
        }
        return squares;
    }

3. 最短單詞路徑

    /*
    * 題目:給定兩個單詞(beginWord 和 endWord)和一個字典,找到從 beginWord 到 endWord 的最短轉換序列的長度。
    * 轉換需遵循如下規則:
    * 每次轉換隻能改變一個字母
    * 轉換過程中的中間單詞必須是字典中的單詞。
    * 說明:
    * 如果不存在這樣的轉換序列,返回 0。所有單詞具有相同的長度。所有單詞只由小寫字母組成。字典中不存在重複的單詞。你可以假設 beginWord 和 endWord 是非空的,且二者不相同。
    *
    * */
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {
        int n = wordList.size();
        /*判斷endWord是否在字典中,並找到所在位置*/
        int endWordIndex = -1;
        for (int i = 0; i < n; i++) {
            String word = wordList.get(i);
            if (word.equals(endWord)) {
                endWordIndex = i;
                break;
            }
        }
        if (endWordIndex == -1) return 0;
        /*建立一個連通圖,graph[i]中存儲着第i個詞可轉化爲字典中其它哪些詞*/
        wordList.add(beginWord);
        n = wordList.size();
        List<Integer> gragh[] = new List[n];
        for (int i = 0; i < n; i++) {
            List<Integer> curlist = new LinkedList<>();
            String curWord = wordList.get(i);
            for (int j = 0; j < i; j++) {
                String otherWord = wordList.get(j);
                if (isConnect(curWord, otherWord)) {
                    curlist.add(j);
                    gragh[j].add(i);
                }
            }
            gragh[i] = curlist;
        }
        /*廣度優先搜索*/
        return findLadderLength(gragh, wordList, endWordIndex);
    }

    public int findLadderLength(List<Integer>[] gragh, List<String> wordList, int endWordIndex) {
        Queue<Integer> queue = new LinkedList<>();
        int n = gragh.length;
        boolean book[] = new boolean[n];
        queue.add(n - 1);
        int path = 1;
        while (!queue.isEmpty()) {
            int size = queue.size();
            path++;
            while (size-- != 0) {
                int index = queue.poll();
                List<Integer> linkit = gragh[index];
                for (int curlinkIndex : linkit) {
                    if (curlinkIndex == endWordIndex) return path;
                    if (book[curlinkIndex]) continue;
                    queue.add(curlinkIndex);
                    book[curlinkIndex] = true;
                }
            }
        }
        return 0;
    }

    /*判斷s1和s2是否可以互相轉化*/
    public boolean isConnect(String s1, String s2) {
        int differ = 0;
        for (int i = 0; i < s1.length(); i++) {
            if (s1.charAt(i) != s2.charAt(i)) {
                differ++;
            }
        }
        return differ == 1;
    }

參考:github—CyC2018題解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章