給定二叉樹和兩個節點n1和n2,編寫程式以找到他們的最近公共祖先。
在做這個之前, 有些東西需要提前問清楚, 二叉樹是不是二叉搜尋樹, 如果是二叉搜尋樹, 那就好處理多了. 在保證2個節點都屬於此二叉樹的情況下, 由於二叉搜尋樹是排序過的, 位於左子樹的結點都比父結點小, 而位於右子樹的結點都比父結點大,node.data>node.left.data && node.data>node.right.data, 我們只需要從樹的根結點開始和兩個輸入的結點進行比較。
如果當前結點的值比兩個結點的值都大,那麼最低的共同父結點一定是在當前結點的左子樹中,於是下一步遍歷當前結點的左子結點。
如果當前結點的值比兩個結點的值都小,那麼最低共同父結點一定在當前結點的右子樹中,於是下一步遍歷當前結點的右子結點。
這樣在樹中從上到下找到的第乙個在兩個輸入結點的値之間的結點,就是最低的公共祖先。
/// 二叉搜尋樹找到公共節點, 前提是這2個節點肯定在二叉樹中, 如果左右節點不存在也會有列印
- (treenode *)__findcommontreenode:(treenode *)tree withfirstdata:(int)firstdata seconddata:(int)seconddata else if (tree.data < firstdata && tree.data < seconddata) else
}return nil;
}這種搜尋方式的時間複雜度是 o(樹的深度), 時間效率還是挺好的.
如果面試官說不是二叉搜尋樹, 那就還有乙個需要確認的, 二叉樹的每個節點上有沒有指向父節點的指標, 如果有, 那麼問題可以簡化很多.
我們可以不考慮這個是二叉樹, 把n1, n2節點的parentnode串起來組成2個鍊錶, 就可以把問題轉化成求這2個鍊錶的第乙個公共節點了.更多方法可以看這個, , 此處放了乙個hash法的, 時間複雜度是o(2個鍊錶的長度和)
把其中乙個鍊錶放到set中, 然後遍歷另乙個鍊錶, 在遍歷過程中找到了set中包含的元素, 那就說明是第乙個公共節點了
// 雜湊表/set,o(m+n)
- (void)__findcommonnodehash:(node *)firstnode secondnode:(node *)secondnode
nsmutableset * set = [nsmutableset set];
while (firstnode)
while (secondnode)
secondnode = secondnode.next;
}nslog(@"hash法 沒找到");
}
如果面試官說不行, 沒有指向父節點的指標, 就是乙個最普通的二叉樹, 那就用更通用的解法.
方案1: 儲存從根節點到n1, n2 的路徑, 比如找1和7的公共節點, 根節點到1的路徑是10->6->4->1 , 根節點到7的路徑是10->6->8->7, 最近的公共祖先就是6,
現在問題來了, 怎麼儲存這個路徑, 可以參考 , 大致就是採用前序遍歷, 然後儲存每個遍歷到的節點加入到陣列中, 如果這個節點 == n1那就對陣列進行一次copy操作, 遍歷到n2也copy一次, 經過copy之後的陣列就是從根節點到n1, n2 的路徑了, 在從路徑中找最後乙個相同的節點即可. 這個需要對樹遍歷一次, 時間複雜度是o(n). 需要額外的空間儲存路徑, 空間上也是o(n).
/// 普通二叉樹找到,
- (treenode *)__findcommontreenode2:(treenode *)tree withfirstdata:(int)firstdata seconddata:(int)seconddata
方案2: 如果說輔助空間都不讓用了, 還可以這樣, 如果在其中的乙個節點 左側找到了n1 || n2 , 右側也找到了n1 || n2中的乙個, 那這個節點就是最近公共祖先了,
舉例來說, n1 = 1, n2 = 7, 這2個節點都在10的左側, 10的右側沒有, 所以10不是, 繼續向左側尋找, 10的左側是6, 6的左側找到了1, 6的右側找到了7, 6就是最近公共祖先了.
總結一下策略,時間複雜度的話, 只需要一次遍歷樹即可 , 時間複雜度是o(n),空間複雜度是o(1).遞迴結束的標誌就是, 在某個節點的左字數發現乙個節點, 右子樹發現乙個節點, 那就是此節點了;
如果左子樹發現了, 右子樹沒有發現, 那就繼續往左子樹尋找;
如果右子樹發現了, 左子樹沒有發現, 那就繼續往右子樹尋找;
/// 普通二叉樹找到, 不使用額外空間
- (treenode *)__findcommontreenode3:(treenode *)tree withfirstdata:(int)firstdata seconddata:(int)seconddata
if (tree == nil || tree.data == firstdata || tree.data == seconddata)
treenode * left = [self __findcommontreenode3:tree.leftnode withfirstdata:firstdata seconddata:seconddata];
nslog(@"在%d左側尋找,找到了%d",tree.data,left.data);
treenode * right = [self __findcommontreenode3:tree.rightnode withfirstdata:firstdata seconddata:seconddata];
nslog(@"在%d右側尋找,找到了%d",tree.data,right.data);
// 左邊找到乙個值, 右邊找到乙個值, 說明就是結果了
if (left && right)
// 左邊找到了,右邊沒找到, 繼續在左邊尋找
if (left)
// 右邊找到了,左邊沒找到, 繼續在右邊尋找
二叉樹 找出2個節點的最近公共祖先
給定二叉樹 不是二叉搜尋樹 和兩個節點n1和n2,編寫程式以找到他們的最近公共祖先 lowest common ancestor,lca 最近公共祖先是兩個節點所有公共祖先中離根節點最遠的節點。計算節點的最近公共祖先是很有用的。例如,為了確定樹中節點之間距離 從n1節點到n2節點的距離,可以計算從根...
二叉樹 最近公共父節點
給定二叉樹中的任意兩點,求解該兩點的最近公共父節點。程式大致分為遞迴和非遞迴兩種方式,下面我們先來認識非遞迴的方式。非遞迴程式總體的思路 用一種常見高效的資料結構來記錄下二叉樹中的結構關係,由於是找父節點,所以要用子節點來查詢父節點,我們這裡用到的是hashmap來進行樹形關係的儲存。接下來就是先列...
二叉樹的最近公共父節點
給定一棵二叉樹和兩個節點,找出這兩個節點最近的乙個公共父節點。給出的兩個節點一定在樹中存在 結點的值是隨機的,可能會重複,結點中只包含left和right兩個子結點,沒有指向父節點的parent。此題可參考leetcode 236。考慮兩個結點的存在情況 乙個節點為是另外乙個節點的子或孫子節點,此時...