一、題目內容:
題目要求找到一個二叉樹的最底層最左邊節點的值。具體來說,我們需要從根節點開始遍歷二叉
樹,找到最深的那層中的最左邊的節點,并返回該節點的值。因為要先找到最底層左側的值,所以我們選擇遍歷順序一定是左側先遍歷,右側后遍歷。
我們需要聲明depth、MaxDepth和result,depth記錄當前深度、MaxDepth記錄最大深度深度、result記錄深度最大值,但在遍歷中我么會思考到,如果遍歷到某一葉子節點,但是當前結點深度并不是最大的,那么遞歸會回退到其父結點,這時就需要將深度回溯,這一過程如何實現呢,我們會在代碼中體現。
二、題目分析
輸入和輸出
-
輸入:二叉樹的根節點
root
。-
二叉樹的每個節點是一個
TreeNode
對象,包含:-
val
:節點的值(整數)。 -
left
:指向左子節點的指針。 -
right
:指向右子節點的指針。
-
-
-
輸出:最底層最左邊節點的值(整數)。
深度優先遍歷函數 trevesal
:
-
參數:
-
root
:當前節點。 -
depth
:當前節點的深度。
-
-
邏輯:
-
如果當前節點是葉子節點(即沒有左子節點和右子節點)則:
-
如果當前深度
depth
大于maxDepth
,則更新maxDepth
和result
。
-
-
如果當前節點有左子節點則:
-
遞歸調用
trevesal
,深度加1。
-
-
如果當前節點有右子節點則:
-
遞歸調用
trevesal
,深度加1。
-
-
回溯:在遞歸調用結束后,深度減1,以恢復到當前節點的深度。
-
三、代碼解答
1.C++代碼
class Solution {
public:int maxDepth = INT_MIN; // 用于記錄當前找到的最深的葉子節點的深度int result; // 用于存儲最底層最左邊節點的值// 定義遞歸函數,用于深度優先搜索void trevesal(TreeNode *root, int depth) {// 如果當前節點是葉子節點(沒有左子節點和右子節點)if (root->left == NULL && root->right == NULL) {// 如果當前深度大于已知的最大深度if (depth > maxDepth) {result = root->val; // 更新結果值為當前節點的值maxDepth = depth; // 更新最大深度為當前深度}}// 如果當前節點有左子節點if (root->left) {depth++; // 深度加1,進入下一層trevesal(root->left, depth); // 遞歸遍歷左子節點depth--; // 回溯,恢復到當前節點的深度}// 如果當前節點有右子節點if (root->right) {depth++; // 深度加1,進入下一層trevesal(root->right, depth); // 遞歸遍歷右子節點depth--; // 回溯,恢復到當前節點的深度}}// 主函數,用于調用遞歸函數并返回結果int findBottomLeftValue(TreeNode* root) {trevesal(root, 0); // 從根節點開始,深度為0return result; // 返回最底層最左邊節點的值}
};
詳細注釋
1. 成員變量
int maxDepth = INT_MIN; // 初始化為最小整數,表示當前找到的最深的葉子節點的深度
int result; // 用于存儲最底層最左邊節點的值
-
maxDepth
:記錄當前找到的最深的葉子節點的深度,初始值為INT_MIN
,確保任何葉子節點的深度都會比它大。 -
result
:存儲最底層最左邊節點的值,初始值未定義,會在遞歸過程中被賦值。
2. 遞歸函數?trevesal
void trevesal(TreeNode *root, int depth) {// 如果當前節點是葉子節點(沒有左子節點和右子節點)if (root->left == NULL && root->right == NULL) {// 如果當前深度大于已知的最大深度if (depth > maxDepth) {result = root->val; // 更新結果值為當前節點的值maxDepth = depth; // 更新最大深度為當前深度}}// 如果當前節點有左子節點if (root->left) {depth++; // 深度加1,進入下一層trevesal(root->left, depth); // 遞歸遍歷左子節點depth--; // 回溯,恢復到當前節點的深度}// 如果當前節點有右子節點if (root->right) {depth++; // 深度加1,進入下一層trevesal(root->right, depth); // 遞歸遍歷右子節點depth--; // 回溯,恢復到當前節點的深度}
}
-
葉子節點檢查:
-
如果當前節點沒有左子節點和右子節點,說明它是一個葉子節點。
-
如果當前葉子節點的深度大于已知的最大深度
maxDepth
,則更新result
和maxDepth
。
-
-
遞歸遍歷左子節點:
-
如果當前節點有左子節點,深度加1,遞歸調用
trevesal
遍歷左子節點。 -
遞歸返回后,深度減1,恢復到當前節點的深度。這是回溯的關鍵步驟,確保每次遞歸返回后,深度值正確。
-
-
遞歸遍歷右子節點:
-
如果當前節點有右子節點,深度加1,遞歸調用
trevesal
遍歷右子節點。 -
遞歸返回后,深度減1,恢復到當前節點的深度。同樣,這是回溯的關鍵步驟。
-
3. 主函數?findBottomLeftValue
int findBottomLeftValue(TreeNode* root) {trevesal(root, 0); // 從根節點開始,深度為0return result; // 返回最底層最左邊節點的值
}
-
調用遞歸函數:
-
從根節點開始,初始深度為0,調用
trevesal
函數。
-
-
返回結果:
-
遞歸完成后,返回
result
,即最底層最左邊節點的值。
-
回溯和遞歸的詳細解釋
遞歸
-
遞歸是一種函數調用自身的方法,用于解決復雜問題。在本題中,遞歸用于遍歷二叉樹的每個節點。
-
每次遞歸調用時,深度
depth
增加1,表示進入下一層。 -
遞歸調用的終止條件是當前節點是葉子節點(沒有左子節點和右子節點)。
回溯
-
回溯是一種在遞歸調用返回后恢復狀態的機制。
-
在本題中,每次遞歸調用返回后,深度
depth
減1,恢復到當前節點的深度。 -
這樣可以確保每次遞歸返回后,深度值正確,不會影響后續的遞歸調用。
示例
假設二叉樹如下:
1/ \2 3/ / \
4 5 6
遍歷過程
-
從根節點
1
開始,深度為0。-
調用
trevesal(1, 0)
。
-
-
遍歷左子節點
2
,深度為1。-
調用
trevesal(2, 1)
。
-
-
遍歷左子節點
4
,深度為2。-
調用
trevesal(4, 2)
。 -
4
是葉子節點,且深度大于maxDepth
,更新maxDepth=2
和result=4
。 -
返回到節點
2
,深度減1,恢復為1。
-
-
返回到根節點
1
,深度減1,恢復為0。 -
遍歷右子節點
3
,深度為1。-
調用
trevesal(3, 1)
。
-
-
遍歷左子節點
5
,深度為2。-
調用
trevesal(5, 2)
。 -
5
是葉子節點,但深度等于maxDepth
,不更新。 -
返回到節點
3
,深度減1,恢復為1。
-
-
遍歷右子節點
6
,深度為2。-
調用
trevesal(6, 2)
。 -
6
是葉子節點,但深度等于maxDepth
,不更新。 -
返回到節點
3
,深度減1,恢復為1。
-
-
返回到根節點
1
,深度減1,恢復為0。
最終,result=4
,即最底層最左邊的節點值。
總結
通過遞歸和回溯,代碼能夠有效地找到二叉樹最底層最左邊的節點值。遞歸用于遍歷二叉樹,回溯用于恢復狀態,確保每次遞歸返回后,深度值正確。