二叉樹中找出2個節點的最近公共祖先

2021-10-08 06:07:34 字數 3448 閱讀 3646

給定二叉樹和兩個節點n1n2,編寫程式以找到他們的最近公共祖先。

在做這個之前, 有些東西需要提前問清楚, 二叉樹是不是二叉搜尋樹, 如果是二叉搜尋樹, 那就好處理多了. 在保證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。考慮兩個結點的存在情況 乙個節點為是另外乙個節點的子或孫子節點,此時...