目錄
- 題目
- 思考分析
- 改進
本文章代碼思路來源于公眾號【代碼隨想錄】
題目
給定一個二叉樹, 找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義為:“對于有根樹 T 的兩個結點 p、q,最近公共祖先表示為一個結點 x,滿足 x 是 p、q 的祖先且 x
的深度盡可能大(一個節點也可以是它自己的祖先)。”
思考分析
1、自下而上遍歷對于此題有極大幫助,而后序遍歷的特征便是優先處理葉子結點
2、如何判斷一個節點是結點q和結點p的公共祖先:
如果找到一個結點,左子樹出現結點p,右子樹出現結點q,或者左子樹出現結點q,右子樹出現結點p。
確定返回值和參數
如果單純只是告訴我們是否找到結點q或者p,那么返回值為bool就行了。
但是我們還要返回最近公共結點,所以返回值為TreeNode* 類型,如果遇到p或者q,就把p或者q返回,返回值不為空就說明了找到了q或者p。
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
確定終止條件
如果找到結點p或者結點q或者結點為空,返回
if(root == q || root == p || root == NULL) return root;
確定單層邏輯
首先確定一點我們需要遍歷樹的所有結點。
直觀上講,如果找到了最近公共祖先,直接一路返回就可以了。如下圖
但是事實上還要遍歷根結點的右子樹(即使現在已經找到了目標結點),這是因為在后序遍歷中,如果想要利用left和right做邏輯處理,不能立即返回,而是要等到left和right的邏輯處理完之后才能返回。
left = 遞歸函數(root->left);
right = 遞歸函數(root->right);
left 和 right 的邏輯處理
所以我們要遍歷整棵樹。
TreeNode* left = lowestCommonAncestor(root->left,p,q);
TreeNode* right= lowestCommonAncestor(root->right,p,q);
1、如果left和right都不為空,說明此時root就是最近公共祖先。
2、如果left為空,right不為空,就返回right,說明目標結點是通過right返回的,反之依然。
3、如果left和right都為空,則返回left或者right都是可以的,也就是返回空。
if(left == NULL && right != NULL) return right;
else if(left != NULL && right ==NULL) return left;
else return NULL; //此時只有left和right同時為空
整個遍歷思路:
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*///如果一個結點不是另外一個結點的祖先,那么他們的最近祖先的深度一定是深度最小結點的深度-1//如果一個結點是另外一個結點的祖先,那么最近祖先就是是祖先的那個結點。
class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if(root == q || root == p || root == NULL ) return root;TreeNode* left = lowestCommonAncestor(root->left,p,q);TreeNode* right = lowestCommonAncestor(root->right,p,q);if(left != NULL && right != NULL) return root;else if(left == NULL && right != NULL) return right;else if(left != NULL && right ==NULL) return left;else return NULL; //此時只有left和right同時為空}
};
改進
在遍歷完左子樹之后,若是返回的left不是NULL,也不是p和q,則說明在左子樹中已經找到了公共祖先了,此時可以直接返回left的值,無需再遍歷右子樹。這就是剪枝操作。
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode(int x) : val(x), left(NULL), right(NULL) {}* };*///如果一個結點不是另外一個結點的祖先,那么他們的最近祖先的深度一定是深度最小結點的深度-1//如果一個結點是另外一個結點的祖先,那么最近祖先就是是祖先的那個結點。
class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if(root == q || root == p || root == NULL ) return root;TreeNode* left = lowestCommonAncestor(root->left,p,q);//*****************剪枝操作******************if(left !=NULL && left !=p && left != q) return left; //*******************************************TreeNode* right = lowestCommonAncestor(root->right,p,q);if(left != NULL && right != NULL) return root;else if(left == NULL && right != NULL) return right;else if(left != NULL && right ==NULL) return left;else return NULL; //此時只有left和right同時為空}
};
加入打印信息,對遍歷的過程更加了解熟悉;
class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if(root == NULL) cout<<"NULL"<<endl;else cout<<root->val<<endl;if(root == q || root == p || root == NULL ) return root;TreeNode* left = lowestCommonAncestor(root->left,p,q);//*****************剪枝操作******************if(left !=NULL && left !=p && left != q) return left; //*******************************************TreeNode* right = lowestCommonAncestor(root->right,p,q);if(left != NULL && right != NULL) return root;else if(left == NULL && right != NULL) return right;else if(left != NULL && right ==NULL) return left;else return NULL; //此時只有left和right同時為空}
};
這一題感覺之后還需要多看幾遍,多體會體會,感覺有些地方還是沒理解。
若是有新的理解,再更新。