最近幾周摻雜着需求、以及一些瑣碎的事情,算法的學習一直都是默默的在搞,沒有形成文章。
或許是我懶惰了;或許是我鬆懈了;或許是我不重視了;但是,我還在。學習可能會因爲工作推遲,但絕不會遲到,所以,還請各位放心,該有的都會到來。
之前也在有些羣裏看到算法的持續學習,我自己又找了一個方式來攻克LeetCode上的題目,先從騰訊精選練習(50題) 開始,之前有完成過一些,不過都是整合在ARTS打卡里,也沒細說。現在是一個系列,還有機會學習噠。
Algorithm LeetCode算法
235. 二叉搜索樹的最近公共祖先
(https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
)
題目描述:給定一個二叉搜索樹,找到該樹中兩個指定節點的最近公共祖先。
百度百科中最近公共祖先的定義爲:“對於有根樹 T 的兩個結點p、q,最近公共祖先表示爲一個結點 x,滿足 x 是 p、q 的祖先且 x 的深度儘可能大(一個節點也可以是它自己的祖先)。”
6
/ \
2 8
/ \ / \
0 4 7 9
/ \
3 5
示例1:
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
輸出: 6
解釋: 節點 2 和節點 8 的最近公共祖先是 6。
示例2:
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
輸出: 2
解釋: 節點 2 和節點 4 的最近公共祖先是 2, 因爲根據定義最近公共祖先節點可以爲節點本身。
在題解之前,我們還是有必要知道二叉搜索樹是一顆什麼樣的樹,都有什麼特點。
二叉搜索樹(Binary Search Tree),又(二叉查找樹,二叉排序樹)它或者是一顆空樹,或者是具有下列性質的二叉樹:
- 若它的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
- 若它的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
- 它的左、右子樹分別爲二叉排序樹。
最近公共祖先: 是距離兩個節點最近的公共祖先節點。在這裏最近考慮的是節點的深度。
比如,在我們這顆二叉樹中
- 如果p是2,q是8的情況,分別在跟節點的左右兩邊,則最近的公共節點就是他們的根節點,即6;
- 如果p是2,q是7的情況,因爲還是分別在6這個根節點的左右兩邊,那最近的公共節點還是6;
- 如果p是2,q是4的情況,2和4在節點2的右子樹上,即最小公共節點就是2自己了(一個節點也可以是它自己的祖先)
- 如果p是2,q是0的情況,2和0在節點2的左子樹上,即最小公共節點還是2自己
好了,搞清楚了各種情況,接下來的解法肯定就難不倒我們了。寫代碼是建立在思路清晰的基礎上,對吧。
解法:遞歸
- 從根節點開始遍歷
- 倘若p和q都在右子樹上,則以右孩子爲根節點繼續1的操作
- 倘若p和q都在左子樹上,則以左孩子爲根節點繼續1的操作
- 如果步驟2和步驟3均不成立,則說明我們已經找到答案
將二叉樹構建完成:
// 將二叉樹構建完成
public static void main(String[] args) {
TreeNode treeNode = new TreeNode(6);
treeNode.left = new TreeNode(2);
treeNode.left.left = new TreeNode(0);
treeNode.left.right = new TreeNode(4);
treeNode.left.right.left = new TreeNode(3);
treeNode.left.right.right = new TreeNode(5);
treeNode.right = new TreeNode(8);
treeNode.right.left = new TreeNode(7);
treeNode.right.right = new TreeNode(9);
TreeNode treeNode2 = lowestCommonAncestor(treeNode, new TreeNode(2), new TreeNode(8));
System.out.println(treeNode2);
}
遞歸查找二叉樹:
public static TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
int parent = root.val;
int pValue = p.val;
int qValue = q.val;
// 如果兩個值都大於跟節點,則從右子樹查找
if (pValue > parent &&qValue > parent) {
return lowestCommonAncestor(root.right, p, q);
// 如果兩個值都小於跟節點,則從左子樹查找
} else if (pValue < parent && qValue < parent) {
return lowestCommonAncestor(root.left, p, q);
// 上述兩種情況均沒有,則得到答案
} else {
return root;
}
}
這裏僅僅舉例說明了通過遞歸的方式來解題,基本上也都能解決二叉樹相關的問題。但是,就這樣就夠了嗎?其實並沒有,二叉樹也有自己的特性,有時候是不需要通過遞歸來解決的。
在我們這個題解裏,時間複雜度是O(N),空間複雜度是O(N),時間複雜度沒辦法了,N是節點中的個數,在最壞的情況下我們是需要訪問所有的節點,但是我們可以優化空間複雜度。
遞歸的時候,額外空間主要是棧產生的,N就是二叉樹的高度,最壞情況下就是訪問所有的,但是我們可以通過另一種方式來優化,空間複雜度可以達到O(1)。歡迎在留言區和我互動,至於寫法,我會在知識星球給出,加入星球的小夥伴直接去看就行啦。
結語
二叉搜索樹,算是對二叉樹的一個延伸了,但是呢,解題的思路還是大同小異,無非就是二叉搜索樹更加有自己的特點,我們操作起來反而會目的性更明確一些,也更好去理解。
LeetCode真的是一個好的學習社區,無論在誰的眼裏,都是學習算法的好去處。小編也無數次的說過,算法就是一個持續學習的過程,只要持續學習,持續練習,持續寫代碼,你就能懂其中的解法,對於我們程序員的思維來說,是一個極大的提升,歡迎和大家一起學習。
我們的矩陣公衆號:奔跑吧攻城獅
簡介:專注於IT開發,和你聊聊職場話題,侃侃球,在休閒和知識的海洋裏遨遊
社羣slogan:社羣是小家,成長靠大家;人人共參與,攜手圖共贏
個人slogan:當你的才華還無法撐起你的野心時候,那應該靜下心來好好學習