Given a binary tree, write a function to get the maximum width of the given tree. The width of a tree is the maximum width among all levels. The binary tree has the same structure as a full binary tree, but some nodes are null.
The width of one level is defined as the length between the end-nodes (the leftmost and right most non-null nodes in the level, where the null nodes between the end-nodes are also counted into the length calculation.
Example 1:
Input:
1
/ \
3 2
/ \ \
5 3 9
Output: 4
Explanation: The maximum width existing in the third level with the length 4 (5,3,null,9).
Example 2:
Input:
1
/
3
/ \
5 3
Output: 2
Explanation: The maximum width existing in the third level with the length 2 (5,3).
Example 3:
Input:
1
/ \
3 2
/
5
Output: 2
Explanation: The maximum width existing in the second level with the length 2 (3,2).
Example 4:
Input:
1
/ \
3 2
/ \
5 9
/ \
6 7
Output: 8
Explanation:The maximum width existing in the fourth level with the length 8 (6,null,null,null,null,null,null,7).
Note: Answer will in the range of 32-bit signed integer.
這道題讓我們求二叉樹的最大寬度,根據題目中的描述可知,這里的最大寬度不是滿樹的時候的最大寬度,如果是那樣的話,肯定是最后一層的結點數最多。這里的最大寬度應該是兩個存在的結點中間可容納的總的結點個數,中間的結點可以為空。那么其實只要我們知道了每一層中最左邊和最右邊的結點的位置,我們就可以算出這一層的寬度了。所以這道題的關鍵就是要記錄每一層中最左邊結點的位置,我們知道對於一棵完美二叉樹,如果根結點是深度1,那么每一層的結點數就是 2*n-1,那么每個結點的位置就是 [1, 2*n-1] 中的一個,假設某個結點的位置是i,那么其左右子結點的位置可以直接算出來,為 2*i 和 2*i+1,可以自行帶例子檢驗。由於之前說過,我們需要保存每一層的最左結點的位置,那么我們使用一個數組 start,由於數組是從0開始的,我們就姑且認定根結點的深度為0,不影響結果。我們從根結點進入,深度為0,位置為1,進入遞歸函數。首先判斷,如果當前結點為空,那么直接返回,然后判斷如果當前深度大於 start 數組的長度,說明當前到了新的一層的最左結點,我們將當前位置存入 start 數組中。然后我們用 idx - start[h] + 1 來更新結果 res。這里 idx 是當前結點的位置,start[h] 是當前層最左結點的位置。然后對左右子結點分別調用遞歸函數,注意左右子結點的位置可以直接計算出來,代碼參見評論區二樓。我們也可以讓遞歸函數直接返回最大寬度了,但是解題思路沒有啥區別,代碼參見評論區三樓。這兩種方法之前都能通過 OJ,直到后來加了一個很極端的 test case,使得二者都整型溢出了 Signed Integer Overflow,原因是這里每層都只有1個結點,而我們代碼中坐標每次都要乘以2的,所以到32層以后就直接溢出了。為了避免溢出的問題,需要做些優化,就是要統計每層的結點數,若該層只有一個結點,那么該層結點的坐標值重置為1,這樣就可以避免溢出了。所以 DFS 的解法就不能用了,只能用層序遍歷,迭代的方法來寫,這里使用了隊列 queue 來輔助運算,queue 里存的是一個 pair,結點和其當前位置,在進入新一層的循環時,首先要判斷該層是否只有1個結點,是的話重置結點坐標位置,再將首結點的位置保存出來當作最左位置,然后對於遍歷到的結點,都更新右結點的位置,遍歷一層的結點后來計算寬度更新結果 res,參見代碼如下:
class Solution { public: int widthOfBinaryTree(TreeNode* root) { if (!root) return 0; int res = 0; queue<pair<TreeNode*,int>> q; q.push({root, 1}); while (!q.empty()) { if (q.size() == 1) q.front().second = 1; int left = q.front().second, right = left, n = q.size(); for (int i = 0; i < n; ++i) { auto t = q.front().first; right = q.front().second; q.pop(); if (t->left) q.push({t->left, right * 2}); if (t->right) q.push({t->right, right * 2 + 1}); } res = max(res, right - left + 1); } return res; } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/662
參考資料:
https://leetcode.com/problems/maximum-width-of-binary-tree/
https://leetcode.com/problems/maximum-width-of-binary-tree/discuss/327721/cpp-bfs-solution
