1. 程式人生 > >巧用深度優先遍歷(DFS)查詢兩個結點的最近公共祖先

巧用深度優先遍歷(DFS)查詢兩個結點的最近公共祖先

巧用深度優先遍歷(DFS)查詢兩個結點的最近祖先

今天在論壇上看到一個問題:已知一棵鏈式儲存的二叉樹上的兩個結點p、q,求解如何快速找到他們的公共祖先。

   說實話,我的第一個念頭就是吐槽為什麼不用陣列儲存二叉樹呢?那樣的話只要分別讀這兩個結點所在位置,然後模仿輾轉相除/相減的運算方法不斷把比較大的那個數字整除2,直到兩個數相等位置,相等的那個數字就是p、q的最近祖先了。

回到問題本身,現實狀況就是這課二叉樹現成就是用連結串列儲存的,總不能為了找 最近祖先 就把連結串列轉換成陣列吧,太費力了。

   我想到的最直接的方式就是分別輸出p和q的路徑,然後從前往後逐個比較判斷是否一致,每一個一致的都是p和q的公共祖先,最近公共祖先自然就是最後一個滿足條件的咯!

這個方法相當費力,需要先遞迴遍歷找到p、q,再向上遞迴輸出路徑。時空複雜度都很高。所以二話不說我就用上百度了。
受劃線處啟發
可惜詞條語焉不詳,我的知識程度也很有限完全理解不了那些虛擬碼的含義思路。不過受劃線處那句話啟發,我突然發現,其實執行一次遍歷就可以分別經過結點p、q以及他們的所有祖先。
下面結合網上隨手下的一棵二叉樹(圖T-1)談談我的思路。
T-1
Test1:尋找4和13的最近公共祖先

   -使用DFS遍歷當我們來到13的時候發現第一個我們要尋找的結點,初始化temp{一個用於儲存祖先資訊的快取器}為13
   -然後向下遍歷發現沒結點了,回到13的父節點10,更新temp為10
   -10沒有其他子結點所以繼續向上遞迴,並更新temp為6
   -訪問6的另一個子結點11,不是我們要找的,繼續向下;
   -11沒有其他子結點,所以向上遞迴
   -回到3,更新temp為3;
   -回到1,更新temp為1;
   -去到4,找到第二個結點,輸出temp中儲存的值{1},並結束程式

Test2:尋找6和13的最近祖先

-使用DFS遍歷來到6,發現第一個我們要尋找的結點,初始化temp為6;
-向下遍歷來到10;
-向下遍歷來到13,發現另一個要尋找的結點,輸出temp中的值{6},結束程式

/******************************************/
程式碼實現起來並不複雜,演算法複雜度O(n),其實大多數情況下我們都不需要遍歷完所有節點就可以找到最近祖先了

不曉得這演算法是不是我第一個發現的…如果那麼好運,,,嘿嘿,我可得好好想個名字。