算法導論22.2-7題:樹T=(V,E)的直徑(diameter)定義爲max(u,v),亦即,樹的直徑是樹中所有最短路徑長度中的最大值。試寫出計算樹的直徑的有效算法,並分析算法的運行時間。
如果這裏的樹T是簡單的二叉樹或者多叉樹,我們可以用遞歸來解決(Diameter(tree T)表示T的直徑);
1. 求出T左子樹的最深節點的深度Dleft,求出T右子樹最深節點的深度Dright。
2. 則T的直徑爲:max(Dleft + Dright + 1, Diameter(T->left), Diameter(T->right))
這裏使用遞歸是因爲有可能直徑不經過根節點。時間複雜度爲O(n^2).
下面還有個優化方法,可以使得時間複雜度爲O(n).
Optimized implementation: The above implementation can be optimized by calculating the height in the same recursion rather than calling a height() separately. Thanks to Amar for suggesting this optimized version. This optimization reduces time complexity to O(n).
/*The second parameter is to store the height of tree.
Initially, we need to pass a pointer to a location with value
as 0. So, function should be used as follows:
int height = 0;
struct node *root = SomeFunctionToMakeTree();
int diameter = diameterOpt(root, &height); */
int diameterOpt(struct node *root, int* height)
{
/* lh --> Height of left subtree
rh --> Height of right subtree */
int lh = 0, rh = 0;
/* ldiameter --> diameter of left subtree
rdiameter --> Diameter of right subtree */
int ldiameter = 0, rdiameter = 0;
if(root == NULL)
{
*height = 0;
return 0; /* diameter is also 0 */
}
/* Get the heights of left and right subtrees in lh and rh
And store the returned values in ldiameter and ldiameter */
ldiameter = diameterOpt(root->left, &lh);
rdiameter = diameterOpt(root->right, &rh);
/* Height of current node is max of heights of left and
right subtrees plus 1*/
*height = max(lh, rh) + 1;
return max(lh + rh + 1, max(ldiameter, rdiameter));
}
如果這裏的樹進化爲了圖,該如何求出它的直徑呢?
1. 從任意一個節點u開始做第一遍BFS,得到距離u最遠的那個節點v
2. 從節點v開始做第二遍BFS,得到距離v最遠的節點 e, 那 v 到 e 就是直徑。
證明:
1. 如果 u 在直徑路徑上:反證法, 如果v不在直徑上,那根據直徑的定義,必然存在一點v2在直徑上,使得dist(u->v2) 大於 dist(u->v), 而這和BFS算法 v是從u 出發到達的所有節點中離u最遠相矛盾。
2. 如果 u 不在直徑路經上, 反證法,看圖:
u ----- w ------- v
/
x ----------y ----------z
上圖中,u-->v 是第一遍BFS算出來的路徑,x-->z 是直徑路徑,反證法假設v 不在直徑路徑上,如圖所示。根據樹和聯通圖的定義,u->v中必然存在一點w, 和x->z中的某點y 相連通,或者說必然存在一個路徑 w--y ,鏈接uv和xz。
代碼就不寫了,不是很複雜。