演算法筆記 - 樹的直徑

gctiruct發表於2024-03-27

樹的直徑

定義 性質:

樹的直徑是樹上最長的鏈,即樹上任意兩點間距離的最大值,可能有多條。

若樹無邊權,則所有直徑的中點相同。

求法:

兩次 \(DFS\)

第一次,以任意節點為根,搜尋到距離自己最遠的點,這個點就是直徑的一個端點。

第二次,以第一次求得的點為根,搜尋到距離自己最遠的點,這個點就是另一個端點。

長度和路徑可以在 \(DFS\) 中求得。

時間複雜度:\(\Theta(N)\)

程式碼:

#include<iostream>
#include<vector>
#include<cstring>
#define int long long
using namespace std;

const int N = 100010;

int n;
vector<int> G[N];
int le, n1, n2, dia[N]; // 直徑長度,一個端點,dfs次數,節點
int de[N];

void predfs(int u, int fa)
{
	if(de[u] > le)
	{
		le = de[u]; // 更新長度
		n2 = u; // 更新端點
	}
	for(int i=0; i<G[u].size(); i++)
	{
		int v = G[u][i];
		if(v == fa)
			continue;
		de[v] = de[u] + 1;
		if(n1)
			dia[v] = u; // 儲存路徑
		predfs(v, u);
	}
}

signed main()
{
	cin >> n;
	for(int i=1; i<n; i++)
	{
		int u, v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	predfs(1, 0);
	memset(de, 0, sizeof(de));
	le = 0, n1 = n2;
	de[n2] = 1;
	predfs(n2, 0);
	cout << n1 << ' ' << n2 << ' ' << le << '\n';
    // 端點和長度
	int t = n2;
	for(int i=1; i<=le; i++)
	{
		cout << t << ' ';
		t = dia[t];
	} // 路徑
	return 0;
}

相關文章