引言
樹作為一種特殊的圖,具有很多良好的性質,樹的直徑便是其中之一。
定義
樹的直徑有許多相近的定義。但由於沒有找到比較權威的定義,就用自己的語言大概表述一下吧。
對於一棵帶非負邊權的樹,定義兩點間距離為兩點間路徑的邊權之和,樹的直徑就是距離最遠的兩點之間的路徑,同時也稱該距離為樹的直徑。
簡而言之,樹的直徑就是樹上最長的簡單路徑。
性質
-
直徑兩端點一定是兩個葉子節點。
-
距離任意點最遠的點一定是直徑的一個端點。
-
對於兩棵樹,如果第一棵樹直徑兩端點為(u,v),第二棵樹直徑兩端點為(x,y),用一條邊將兩棵樹連接,那么新樹的直徑一定是u,v,x,y中的兩個點。
性質證明
-
顯然成立,證明從略。
-
情況一:點a在直徑\((u,v)\)上。若存在點b使得$ dis(a,b) \gt dis (a,u) \text{並且} dis(a,b) \gt dis (a,v)$ ,則有 \(dis(b,v) \text{或} dis(b,u) \gt dis(u,v)\),即\((u,v)\)不是該樹的直徑,矛盾。
情況二:點a不在直徑\((u,v)\)上。若存在點b使得$ dis(a,b) \gt dis (a,u) \text{並且} dis(a,b) \gt dis (a,v)$ ,不妨取直徑的其中一個端點\(u\)分析。
分析圖如下(靈魂畫手上線):
圖片注:\(u\),\(v\) 為直徑端點,\(c\)為\((a,b)\text{與}(a,u)\)交點,\(d\)為\((a,u)\text{與}(v,u)\)交點。
由於\(dis(a,b) \gt dis (a,u)\) ,可知 \(dis(c,b) \gt dis (c,u)\),進而得到\(dis(d,b) \gt dis (d,u)\),最終可知\(dis(v,b) \gt dis (v,u)\),即\((u,v)\)不是該樹的直徑,矛盾。
-
如果新樹直徑不是原來兩棵樹中一棵的直徑,那么新直徑一定經過兩棵樹的連接邊,新直徑在原來每棵樹中的部分一定是距離連接點最遠的點,即一定是原樹直徑的一個端點。
求解算法
共有兩種時間復雜度為 \(O(N)\) 的算法,一種是dp,另一種是貪心算法。
方法一:動態規划
根據定義,我們只需要求出最大鏈長即可。
首先將該樹轉化為有根樹。設\(f[i]\)為以節點\(i\)為根的子樹的最大深度,有狀態轉移方程$$f[i] = \max_{j \in s(i)}f[j]+1 \quad \text{where \(s(i)\) is the set of the son nodes of node i.}$$那么在該子樹中經過節點\(i\)的最長鏈長度為 \(f[i]\) 與次大 \(f[j]\) (缺省值為0)之和。在整個遍歷過程中記錄最大鏈長即可。
方法二:貪心算法
根據樹的直徑性質2,我們只需要任取一點\(s\)開始DFS(或BFS)即可找到距離該點的最遠點,即直徑端點之一,記為\(u\)。同樣的,從節點\(u\)開始我們便可以找到另一個端點\(v\),過程中記錄距離即可得到樹的直徑。
相較於方法一,方法二可以更方便地找到直徑的端點,進而得到直徑的具體路徑。
Code
暫略