We want to use quad trees to store an N x N
boolean grid. Each cell in the grid can only be true or false. The root node represents the whole grid. For each node, it will be subdivided into four children nodes until the values in the region it represents are all the same.
Each node has another two boolean attributes : isLeaf
and val
. isLeaf
is true if and only if the node is a leaf node. The val
attribute for a leaf node contains the value of the region it represents.
Your task is to use a quad tree to represent a given grid. The following example may help you understand the problem better:
Given the 8 x 8
grid below, we want to construct the corresponding quad tree:
It can be divided according to the definition above:
The corresponding quad tree should be as following, where each node is represented as a (isLeaf, val)
pair.
For the non-leaf nodes, val
can be arbitrary, so it is represented as *
.
Note:
N
is less than1000
and guaranteened to be a power of 2.- If you want to know more about the quad tree, you can refer to its wiki.
這道題讓我們根據一個二維數組來建立一棵四叉樹,關於四叉樹的介紹題目中也了給了wiki百科的鏈接。但是博主開始看到題目中給的那個例子,沒怎么看懂。后來分析大神們的代碼,才略微弄明白了一些。原來葉結點表示的是值相同的一片區域,比如我們看二維數組圖示那行的第三個圖,首先整個數組被分成了四等份,左上,左下,和右下部分內的值均相同,那么他們都是一個葉結點,而右上只有再四等分一下,才能使各自部分內的值相同,所以其就不是葉結點,而四等分后的每個區間才是葉結點。題目中限定了N的值一定是2的指數,就是說其如果可分的話,一定可以四等分,而之前說了,只有區間內的值不同時,才需要四等分,否則整體就當作一個葉結點。所以我們需要check四等分區間內的值是否相同,當然,我們可以將二維數組拆分為四個二維數組,但是那樣可能不太高效,而且還占用額外空間,一個比較好的選擇是用坐標變量來控制等分數組的范圍,我們只需要一個起始點坐標,和區間的長度,就可以精確定位一個區間了。比如說對於例子中的整個二維數組數組來說,知道起始點坐標 (0, 0),還有長度8,就知道表示的是哪個區間。我們可以遍歷這個區間上的其他所有的點,跟起點對比,只要有任何點跟起點不相同,則說明該區間是可分的,因為我們前面說了,只有一個區間上所有的值均相同,才能當作一個葉結點。只要有不同,就表示可以四分,那么我們就新建一個結點,這里的左上,左下,右上,和右下四個子結點就需要用過調用遞歸函數來實現了,實現原理都一樣,重要的地方就是確定每個四分區間的起點和長度,長度好確定,都是當前長度的一半,然后就是把各個區間的起點確定好,這個也不難,就是細心一點,不要寫錯了就可以了,另外,對於非葉結點,結點值可以是true或者false都沒問題。如果某個區間上所有值均相同,那么就生成一個葉結點,結點值就跟區間值相同,isLeaf是true,四個子結點均為NULL即可,參見代碼如下:
解法一:
class Solution { public: Node* construct(vector<vector<int>>& grid) { return build(grid, 0, 0, grid.size()); } Node* build(vector<vector<int>>& grid, int x, int y, int len) { if (len <= 0) return NULL; for (int i = x; i < x + len; ++i) { for (int j = y; j < y + len; ++j) { if (grid[i][j] != grid[x][y]) { return new Node(true, false, build(grid, x, y, len / 2), build(grid, x, y + len / 2, len / 2), build(grid, x + len/ 2, y, len / 2), build(grid, x + len / 2, y + len / 2, len / 2)); } } } return new Node(grid[x][y] == 1, true, NULL, NULL, NULL, NULL); } };
還有一種寫法,記錄了區間的左上點和右下點,知道這兩個點也可以確定一個區間的位置,整體思路和上面的方法並沒有什么太大的區別,參見代碼如下:
解法二:
class Solution { public: Node* construct(vector<vector<int>>& grid) { return build(grid, 0, 0, grid.size() - 1, grid.size() - 1); } Node* build(vector<vector<int>>& grid, int r1, int c1, int r2, int c2) { if (r1 > r2 || c1 > c2) return NULL; bool isLeaf = true; int val = grid[r1][c1], rowMid = (r1 + r2) / 2, colMid = (c1 + c2) / 2; for (int i = r1; i <= r2; ++i) { for (int j = c1; j <= c2; ++j) { if (grid[i][j] != val) { isLeaf = false; break; } } } if (isLeaf) return new Node(val == 1, true, NULL, NULL, NULL, NULL); return new Node(false, false, build(grid, r1, c1, rowMid, colMid), build(grid, r1, colMid + 1, rowMid, c2), build(grid, rowMid + 1, c1, r2, colMid), build(grid, rowMid + 1, colMid + 1, r2, c2)); } };
參考資料:
https://leetcode.com/problems/construct-quad-tree/
https://leetcode.com/problems/construct-quad-tree/discuss/151j684/Recursive-Java-Solution
https://leetcode.com/problems/construct-quad-tree/discuss/154420/My-Java-Recursive-Solution