由斜槓劃分區域

題目連接:regions-cut-by-slashesjava

題目描述

在由 1 x 1 方格組成的 N x N 網格 grid 中,每一個 1 x 1 方塊由 /、 或空格構成。這些字符會將方塊劃分爲一些共邊的區域。(請注意,反斜槓字符是轉義的,所以  用 "\" 表示。)。返回區域的數目。c++

題目分析

  1. 該題目用用圖來表示的話,一目瞭然。但是表示成了字符串以後,就感受有點不知從何處下手。
  2. 想辦法將圖形表示轉化成直觀的數組表示法。

算法

並查集(Union Find)


因爲每一個小區域只填充'/'或者'',所以,小區域劃分最多有三種狀況(編號如上圖所示):算法

  1. 編號0和編號1在同一個塊,編號2和編號3在同一個塊。
  2. 編號0和編號2在同一個塊,編號1和編號3在同一個塊。
  3. 編號0, 1, 2, 3都在同一個塊。

相鄰區域間,他們的編號小區域存在必定會在同一個塊的狀況。其中:數組

  1. 編號0和上一個區域編號3必定在同一個塊。
  2. 編號1和左邊區域的編號2必定在同一個塊。
  3. 編號2和右邊區域的編號1必定在同一個塊。
  4. 編號3和下邊區域的編號0必定在同一個塊。

咱們開始給每一個小區域編一個惟一的塊號,經過遍歷區域,達到數出所有惟一塊的數目。ui

代碼

class Solution {
    private int[] grids;            // 用數組來表示塊編號
    private int num;                // 統計惟一塊的數目
    
    public int regionsBySlashes(String[] grid) {
        int N = grid.length;
        buildUnionFind(N);
        
        // 遍歷合併全部的區域
        for (int r = 0; r < grid.length; r++)
            for (int c = 0; c < grid[r].length(); c++)
                process(r, c, N, grid[r].charAt(c));
        return num;
    }
    
    // 構建並查集,初始每一個塊有一個惟一編號
    private void buildUnionFind(int N) {
        grids = new int[N * N * 4];
        num = N * N * 4;
        for (int i = 0; i < grids.length; i++) grids[i] = i;
    }
    
    // 根據行和列的序號,獲得塊在數組中的位置
    private int getSubSquare(int r, int c, int N, int ith) {
        return r * N * 4 + c * 4 + ith;
    }
    
    // 尋找塊的根編號
    private int find(int p) {
        while (grids[p] != p) {
            grids[p] = grids[grids[p]];
            p = grids[p];
        }
        return p;
    }
    
    // 合併兩個塊
    private void union(int p, int q) {
        int rootP = find(p);
        int rootQ = find(q);
        
        if (rootP != rootQ) num--;      // 若是兩個塊擁有不一樣的編號,合併後,惟一塊的數目減1
        grids[rootP] = rootQ;
    }
    

    private void process(int r, int c, int N, char square) {
        // 獲取區域中四個小區域的塊編號
        int s0 = getSubSquare(r, c, N, 0);
        int s1 = getSubSquare(r, c, N, 1);
        int s2 = getSubSquare(r, c, N, 2);
        int s3 = getSubSquare(r, c, N, 3);
        
        // 同一個區域的小區域合併
        switch(square) {
            case '\\':
                union(s0, s2);
                union(s1, s3);
                break;
            case '/':
                union(s0, s1);
                union(s2, s3);
                break;
            default:
                union(s0, s1);
                union(s1, s2);
                union(s2, s3);
        }

        // 相鄰區域的合併
        if (r > 0) {
            union(s0, getSubSquare(r - 1, c, N, 3));
        }
        if (r < N - 1) {
            union(s3, getSubSquare(r + 1, c, N, 0));
        }
        if (c > 0) {
            union(s1, getSubSquare(r, c - 1, N, 2));
        }
        if (c < N - 1) {
            union(s2, getSubSquare(r, c + 1, N, 1));
        }
    }
}

咱們很難從原圖中,將該圖轉化成一個用數字表示的數組。可是,當咱們將原圖表示成一個規模*2的數組時,他將能夠表示以下:code

0 1 0 1
["//", "/ "]    =>      1 0 1 0
                        0 1 0 0
                        1 0 0 0

咱們將數字1當作一道牆,數字0當作一個空白塊,則沒有牆相隔的塊,他們屬於同一個惟一塊。從上數字中,咱們能夠直觀的看出,一共存在3個惟一塊。可是這時,屬於同一個惟一空白塊的小空白塊他們可能還不相鄰(即在斜對角線上)。咱們再把原圖表示成一個規模*3數組,表示以下:blog

0 0 1 0 0 1
                        0 1 0 0 1 0
["//", "/ "]    =>      1 0 0 1 0 0
                        0 0 1 0 0 0
                        0 1 0 0 0 0
                        1 0 0 0 0 0

這時,能夠清楚的看出,一共存在3個惟一空白塊,且在同一個惟一空白塊的全部小空白塊,都是相鄰的了。這個時候,咱們就能夠採用深度優先搜索,從數組的邊界開始,遍歷存在多少個不相鄰的0,便可獲得答案。leetcode

代碼

class Solution {
    int[][] grids;          // 表示原圖的數組
    
    public int regionsBySlashes(String[] grid) {
        buildGrids(grid);
        int count = 0;
        // 深搜遍歷
        for (int r = 0; r < grids.length; r++) {
            for (int c = 0; c < grids[r].length; c++) {
                count += dfs(r, c);
            }
        }
        return count;
    }
    
    // 將原圖表示成規模*3的數組
    private void buildGrids(String[] grid) {
        int N = grid.length;
        grids = new int[N * 3][N * 3];
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[i].length(); j++) {
                if (grid[i].charAt(j) == '/') {
                    grids[i * 3 + 2][j * 3] = 1;
                    grids[i * 3 + 1][j * 3 + 1] = 1;
                    grids[i * 3][j * 3 + 2] = 1;
                } else if (grid[i].charAt(j) == '\\') {
                    grids[i * 3][j * 3] = 1;
                    grids[i * 3 + 1][j * 3 + 1] = 1;
                    grids[i * 3 + 2][j * 3 + 2] = 1;
                }
            }
        }
    }
    
    private int dfs(int r, int c) {
        // 越界返回0
        if (r < 0 || r >= grids.length || c < 0 || c >= grids[r].length) return 0;
        // 遇到牆(數字1)返回0
        if (grids[r][c] == 1) return 0;
        
        // 將空白塊改成1,並向他四周遍歷,結果返回1
        grids[r][c] = 1;
        dfs(r - 1, c);
        dfs(r + 1, c);
        dfs(r, c - 1);
        dfs(r, c + 1);
        return 1;
    }
}
相關文章
相關標籤/搜索