算法:如何算树的直径?

88 阅读2分钟

首先要明确什么是树的直径?

树的直径定义如下: 节点对之间距离最远的距离。

可以想象成一棵树的主干。其他的作为分支挂在主干上。

如何计算树的直径?两次BFS

第一次BFS,从任意一点x开始,沿着x找最远的点A,A就在直径上面。

反证法: 假设直径两点是A和B,从x出发的最远点P不是A或B,那么x-P和A-B一定有一个交叉点y。而y在直径A-B上。因为x-P是x能走的最远距离,那么y-P一定大于y-A和y-B,这样会导致A-y-P会大于A-y-B的路径,那么A-B就不是最远路径了。与我们的假设相反。

第二次BFS从P点出发,最远点就是直径的另一端。 C++代码实现:

#include <bits/stdc++.h>
using namespace std;
 
using ll = long long;
using ull = unsigned long long;
using pii = pair<int,int>;
using pll = pair<ll,ll>;
using vi = vector<int>;
using vll = vector<ll>;
 
#define all(x) (x).begin(), (x).end()
#define rall(x) (x).rbegin(), (x).rend()
#define rep(i,a,b) for (int i = (a); i < (b); ++i)
#define per(i,a,b) for (int i = (a); i >= (b); --i)
#define pb push_back
#define eb emplace_back
#define sz(x) ((int)(x).size())
#define fastio do { ios::sync_with_stdio(false); cin.tie(nullptr); } while (0)
#define endl '\n'
 
#ifdef LOCAL
#define dbg(x) cerr << #x << " = " << (x) << "\n"
#else
#define dbg(x)
#endif
 
const int INF = 1e9;
const ll LINF = 1e18;
const int MOD = 1e9 + 7;
const int MAXN = 2e5;
const int K = 25;

void solve() {
  ll n; std::cin >> n;
  if(n == 1){
    cout << 0 << endl;
    return;
  } 
  vector<vll> g(n);
  rep(i, 0, n - 1) {
    ll x, y; std::cin >> x >> y;
    g[--x].eb(--y);
    g[y].eb(x);
  }
  auto bfs = [&](ll root, vll &dist) {
    dist.assign(n, -1);
    dist[root] = 0;
    queue<ll> q; q.emplace(root);
    while(!q.empty()) {
      ll u = q.front(); q.pop();
      for(const auto &v : g[u]) {
        if(dist[v] == -1) {
          q.emplace(v);
          dist[v] = dist[u] + 1;
        }
      }
    }
  };
  vll dist(n);
  bfs(0, dist);
  ll dmax = -INF, vn = -1;
  rep(i, 1, n) {
    if(dist[i] > dmax) {
      dmax = dist[i];
      vn = i;
    }
  }
  vll dist2(n);
  bfs(vn, dist2);
  dmax = -INF, vn = -1;
  rep(i, 0, n) {
    if(dist2[i] > dmax) {
      dmax = dist2[i];
      vn = i;
    }
  }
  cout << dmax << endl;
}
int main() {
  fastio;
  solve();
  return 0;
}

如有错误,请及时评判指正!