[LeetCode] 302. Smallest Rectangle Enclosing Black Pixels 包含黑像素的最小矩陣


 

An image is represented by a binary matrix with 0 as a white pixel and 1 as a black pixel. The black pixels are connected, i.e., there is only one black region. Pixels are connected horizontally and vertically. Given the location (x, y) of one of the black pixels, return the area of the smallest (axis-aligned) rectangle that encloses all black pixels.

Example:

Input:
[
  "0010",
  "0110",
  "0100"
]
and x = 0, y = 2

Output: 6

 

這道題給我們一個二維矩陣,表示一個圖片的數據,其中1代表黑像素,0代表白像素,現在讓找出一個最小的矩陣可以包括所有的黑像素,還給了一個黑像素的坐標,先來看 Brute Force 的方法,這種方法的效率不高,遍歷了整個數組,如果遇到了1,就更新矩形,參見代碼如下:

 

解法一:

// Brute force
class Solution {
public:
    int minArea(vector<vector<char>>& image, int x, int y) {
        int left = y, right = y, up = x, down = x;
        for (int i = 0; i < image.size(); ++i) {
            for (int j = 0; j < image[i].size(); ++j) {
                if (image[i][j] == '1') {
                    left = min(left, j);
                    right = max(right, j);
                    up = min(up, i);
                    down = max(down, i);
                }
            }
        }
        return (right - left + 1) * (down - up + 1);
    }
};

 

下面這種解法是解法一的遞歸寫法,本質上來說跟上面的解法沒有啥區別,也沒有任何的優化,所以仍然可以認為是暴力搜索法,參見代碼如下:

 

解法二:

// DFS
class Solution {
public:
    int minArea(vector<vector<char>>& image, int x, int y) {
        int left = y, right = y, up = x, down = x;
        dfs(image, x, y, left, right, up, down);
        return (right - left + 1) * (down - up + 1);
    }
    void dfs(vector<vector<char>> &image, int x, int y, int &left, int &right, int &up, int &down) {
        if (x < 0 || x >= image.size() || y < 0 || y >= image[0].size() || image[x][y] != '1') return;
        left = min(left, y); 
        right = max(right, y);
        up = min(up, x);
        down = max(down, x);
        image[x][y] = '2';
        dfs(image, x + 1, y, left, right, up, down);
        dfs(image, x - 1, y, left, right, up, down);
        dfs(image, x, y + 1, left, right, up, down);
        dfs(image, x, y - 1, left, right, up, down);
    }
};

 

我們再來看一種優化了時間復雜度的解法,這是一種二分搜索法,以給定的一個黑像素 (x, y) 為中心,分別用二分法快速找到整個黑色區域的上下左右的臨界點,然后直接算出面積。首先來看上邊界怎么找,既然是以 (x, y) 為中心,而且上邊界又是某個行數,那么其范圍肯定在 [0, x] 之間,能成為上邊界的條件是該行中至少有一個點是1,那么其列數的范圍就在 [0, n] 之間,在進行二分搜索的時候,先根據i, j算出中間行 mid,然后列數從0開始遍歷,直到找到為1的點,或者越界位置,然后判斷列數是否越界,越界的話,說明當前行沒有1,此時更新i為 mid+1,如果找到了1,那么更新j為 mid。找下邊界也是同樣的道理,但是跟上邊界稍微又些不同的地方是,如果當前行找到了1,應該再往下找,那么i應該更新為 mid+1;如果沒找到,就應該往上找,靠近 (x, y) 點;所以兩種情況只是在二分法更新范圍的地方正好相反,所以可以用一個 bool 型的變量 opt 來決定還如何更新行數。

下面來看如何確定左邊界和右邊界,其實跟確定上下邊界大同小異。左邊界是列數,若以 (x, y) 點為中心,那么其范圍便是 [0, y],因為之前已經確定了上下邊界 up 和 down 了,所以左邊界點的行數范圍就是 [up, down],同理,當通過i, j求出了中間列 mid 時,就要遍歷該列,找到為1的點,所以此時是用 image[k][mid],而在找上下邊界時,用的是 image[mid][k],還是順序不一樣,可以用另外一個 bool 型變量h來控制,h表示 horizontal,就是水平遍歷的意思。這樣通過兩個 bool 型變量就可以用一個函數來涵蓋四種情況的二分搜索,是不是很叼?下面更新i或j的時候參考上下邊界的分析,應該不難理解,參見代碼如下:

 

解法三:

// Binary Search
class Solution {
public:
    int minArea(vector<vector<char>>& image, int x, int y) {
        int m = image.size(), n = image[0].size();
        int up = binary_search(image, true, 0, x, 0, n, true);
        int down = binary_search(image, true, x + 1, m, 0, n, false);
        int left = binary_search(image, false, 0, y, up, down, true);
        int right = binary_search(image, false, y + 1, n, up, down, false);
        return (right - left) * (down - up);
    }
    int binary_search(vector<vector<char>> &image, bool h, int i, int j, int low, int high, bool opt) {
        while (i < j) {
            int k = low, mid = (i + j) / 2;
            while (k < high && (h ? image[mid][k] : image[k][mid]) == '0') ++k;
            if (k < high == opt) j = mid;
            else i = mid + 1;
        }
        return i;
    }
};


參考資料:

https://leetcode.com/problems/smallest-rectangle-enclosing-black-pixels/

https://leetcode.com/problems/smallest-rectangle-enclosing-black-pixels/discuss/75128/1ms-Concise-Java-Binary-Search-(DFS-is-4ms)

https://leetcode.com/problems/smallest-rectangle-enclosing-black-pixels/discuss/75127/C%2B%2BJavaPython-Binary-Search-solution-with-explanation

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM