Given a binary tree where every node has a unique value, and a target key k
, find the value of the nearest leaf node to target k
in the tree.
Here, nearest to a leaf means the least number of edges travelled on the binary tree to reach any leaf of the tree. Also, a node is called a leaf if it has no children.
In the following examples, the input tree is represented in flattened form row by row. The actual root
tree given will be a TreeNode object.
Example 1:
Input: root = [1, 3, 2], k = 1 Diagram of binary tree: 1 / \ 3 2 Output: 2 (or 3) Explanation: Either 2 or 3 is the nearest leaf node to the target of 1.
Example 2:
Input: root = [1], k = 1 Output: 1 Explanation: The nearest leaf node is the root node itself.
Example 3:
Input: root = [1,2,3,4,null,null,null,5,null,6], k = 2 Diagram of binary tree: 1 / \ 2 3 / 4 / 5 / 6 Output: 3 Explanation: The leaf node with value 3 (and not the leaf node with value 6) is nearest to the node with value 2.
Note:
root
represents a binary tree with at least1
node and at most1000
nodes.- Every node has a unique
node.val
in range[1, 1000]
. - There exists some node in the given binary tree for which
node.val == k
.
這道題讓我們找二叉樹中最近的葉結點,葉結點就是最底端沒有子結點的那個。我們觀察題目中的例子3,發現結點2的最近葉結點是其右邊的那個結點3,那么傳統的二叉樹的遍歷只能去找其子結點中的葉結點,像這種同一層水平的結點該怎么弄呢?我們知道樹的本質就是一種無向圖,但是樹只提供了父結點到子結點的連接,反過來就不行了,所以只要我們建立了反向連接,就可以用BFS來找最近的葉結點了。明白了這一點后,我們就先來做反向連接吧,用一個哈希map,建立子結點與其父結點之間的映射,其實我們不用做完所有的反向連接,而是做到要求的結點k就行了,因為結點k的子結點可以直接訪問,不需要再反過來查找。我們用DFS來遍歷結點,並做反向連接,直到遇到結點k時,將其返回。此時我們得到了結點k,並且做好了結點k上面所有結點的反向連接,那么就可以用BFS來找最近的葉結點了,將結點k加入隊列queue和已訪問集合visited中,然后開始循環,每次取出隊首元素,如果是葉結點,說明已經找到了最近葉結點,直接返回;如果左子結點存在,並且不在visited集合中,那么先將其加入集合,然后再加入隊列,同理,如果右子結點存在,並且不在visited集合中,那么先將其加入集合,然后再加入隊列;再來看其父結點,如果不在visited集合中,那么先將其加入集合,然后再加入隊列。因為題目中說了一定會有結點k,所以在循環內部就可以直接返回了,不會有退出循環的可能,但是為表尊重,我們最后還是加上return -1吧, 參見代碼如下:
解法一:
class Solution { public: int findClosestLeaf(TreeNode* root, int k) { unordered_map<TreeNode*, TreeNode*> back; TreeNode *kNode = find(root, k, back); queue<TreeNode*> q{{kNode}}; unordered_set<TreeNode*> visited{{kNode}}; while (!q.empty()) { TreeNode *t = q.front(); q.pop(); if (!t->left && !t->right) return t->val; if (t->left && !visited.count(t->left)) { visited.insert(t->left); q.push(t->left); } if (t->right && !visited.count(t->right)) { visited.insert(t->right); q.push(t->right); } if (back.count(t) && !visited.count(back[t])) { visited.insert(back[t]); q.push(back[t]); } } return -1; } TreeNode* find(TreeNode* node, int k, unordered_map<TreeNode*, TreeNode*>& back) { if (node->val == k) return node; if (node->left) { back[node->left] = node; TreeNode *left = find(node->left, k, back); if (left) return left; } if (node->right) { back[node->right] = node; TreeNode *right = find(node->right, k, back); if (right) return right; } return NULL; } };
下面這種解法也挺巧妙的,雖然沒有像上面的解法那樣建立所有父結點的反向連接,但是這種解法直接提前算出來了所有父結點到結點k的距離,就比如說例子3中,結點k的父結點只有一個,即為結點1,那么算出其和結點k的距離為1,即建立結點1和距離1之間的映射,另外建立結點k和0之間的映射,這樣便於從結點k開始像葉結點統計距離。接下來,我們維護一個最小值mn,表示結點k到葉結點的最小距離,還有結果res,指向那個最小距離的葉結點。下面就開始再次遍歷二叉樹了,如果當前結點為空, 直接返回。否則先在哈希map中看當前結點是否有映射值,有的話就取出來(如果有,則說明當前結點可能k或者其父結點),如果當前結點是葉結點了,那么我們要用當前距離cur和最小距離mn比較,如果cur更小的話,就將mn更新為cur,將結果res更新為當前結點。否則就對其左右子結點調用遞歸函數,注意cur要加1,參見代碼如下:
解法二:
class Solution { public: int findClosestLeaf(TreeNode* root, int k) { int res = -1, mn = INT_MAX; unordered_map<int, int> m; m[k] = 0; find(root, k, m); helper(root, -1, m, mn, res); return res; } int find(TreeNode* node, int k, unordered_map<int, int>& m) { if (!node) return -1; if (node->val == k) return 1; int r = find(node->left, k, m); if (r != -1) { m[node->val] = r; return r + 1; } r = find(node->right, k, m); if (r != -1) { m[node->val] = r; return r + 1; } return -1; } void helper(TreeNode* node, int cur, unordered_map<int, int>& m, int& mn, int& res) { if (!node) return; if (m.count(node->val)) cur = m[node->val]; if (!node->left && !node->right) { if (mn > cur) { mn = cur; res = node->val; } } helper(node->left, cur + 1, m, mn, res); helper(node->right, cur + 1, m, mn, res); } };
參考資料:
https://leetcode.com/problems/closest-leaf-in-a-binary-tree/
https://leetcode.com/problems/closest-leaf-in-a-binary-tree/discuss/109960/java-dfs-bfs-27ms