題目連結:
題目意譯:
給定一個大小為 n × n 且只由 0 和 1 所組成的矩陣 grid。我們想要用一個四元樹(Quad-Tree)代表 grid。
回傳代表 grid 的四元樹之根節點。
一個四元樹為一個樹狀資料結構,其中每一個內部節點皆有恰好四個子節點。除此之外,每個節點有兩個性質:
val:如果此節點代表著 grid 某一區域的 1,則其值為真(Ture);如果代表著某一區域的 0,則其值為假(False)。注意到你可以將那些 isLeaf 為假的節點之 val 設為真或假皆可,兩種答案都可被接受。
isLeaf:如果此節點是樹中的一個葉節點,則其值為真;反之如果該節點有四個子節點,則其值為假。
class Node {
public boolean val;
public boolean isLeaf;
public Node topLeft;
public Node topRight;
public Node bottomLeft;
public Node bottomRight;
}
我們可以從一個二維區域經由下列步驟建構出一個四元樹:
如果當前網格有著相同的數值(即全為 1 或全為 0)則將 isLeaf 設為真、 val 設為當前網格的數值並將四個子節點設為空(Null)並停止分切的過程。
如果當前網格有著不同的數值,則將 isLeaf 設為假、 val 設為任意數值並將當前網格分切成四個子網格如下圖。
對於每個子節點以對應的子網格遞迴分切下去。
(譯注:「topLeft」為「左上」、「topRight」為「右上」、「bottomLeft」為「左下」,而「bottomRight」為「右下」)
如果你想要知道更多關於四元樹的資訊,你可以參見
維基頁面。
四元樹的格式:
你不需要閱讀此部分才能解本題。這部分只是為了想瞭解輸出格式的人們。輸出代表著一個四元樹藉由階層探訪(Level-Order Traversal)的序列化(Serialized)格式,其中 null 代表著某個路徑之終點,並且這之後不存在更多節點。
這與二元樹的序列化(Serialization)非常地相似。唯一的差別是單一節點以一個列表 [isLeaf, val] 所表示。
如果 isLeaf 或 val 的數值為真,則在列表 [isLeaf, val] 中我們將以數字 1 表示;而反之若數值為假,則以數字 0 表示。
限制:
n == grid.length == grid[i].length
n == 2 ^ x where 0 ≦ x ≦ 6
範例測資:
範例 1:
輸入: grid = [[0,1],[1,0]]
輸出: [[0,1],[1,0],[1,1],[1,1],[1,0]]
解釋: 這個例子的解釋為以下:
注意到代表四元樹的圖中 0 代表著假而 1 代表著真。
範例 2:
輸入: grid = [[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0],[1,1,1,1,0,0,0,0]]
輸出: [[0,1],[1,1],[0,1],[1,1],[1,0],null,null,null,null,[1,0],[1,0],[1,1],[1,1]]
解釋: grid 中所有數字皆不相同。我們將網格分成四個子網格。
topLeft 、 bottomLeft 和 bottomRight 各自有著相同的數值。
topRight 有著不同的數字在其中,所以我們將其再分切成四個子網格,其中每一個各自有著相同的數值。
下圖表示了此四元樹:
解題思維:
遞迴模擬即可。
由於 grid 大小最大也只有 64 × 64,因此每一次分切時直接重新掃過一次來檢查當前網格數值是否相同也是可被接受的(不需要二維前綴和(Prefix Sums)之類的來加速判斷)。
檢查完之後,如果一樣則停止分切;反之則繼續按照題目的要求來分切(由於題目的條件之緣故,因此不需要考慮沒辦法切得剛剛好)。
此次分享到此為止,如有任何更加簡潔的想法或是有說明不清楚之地方,也煩請各位大大撥冗討論。