[LeetCode] Construct Quad Tree 建立四叉樹
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
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)); } };
參考資料: