求树的直径算法

求树的直径算法

标签: 图论——树的直径
阅读体验:https://zybuluo.com/Junlier/note/1251025
嗯,上次考试才发现自己连树的直径都只会\(Floyd\)......(弱到家了......)

树的直径

树的直径是树上的最长路

求法:2遍\(Dfs(Bfs)\)

没错,真的这么简单......

  1. 先随便找个点i开始\(Dfs\),然后找到一条最长路径(假设终点是\(u\))
  2. 然后从u开始再一次\(Dfs\),再找到一条最长路径(假设终点是\(v\)),\((u,v)\)就是树的直径了......
    PS:树的直径可以有多条(想想定义就知道)

证明:为什么呢?

假设树的直径是\((u,v)\),第一次\(Dfs\)找到\((i,j)\)
如果我们\(Dfs\)找到了树的直径的一端\((u/v)\),那么第二次就一定可以找到树的直径对吧
那么证明转化成了第一次\(Dfs\)是否找到了树的直径的一端\((u/v)\)
PS:建议自己手动画棵树来一边看着证明 效果更佳

首先假设i在最长路径上:

  • 反证法:
  • 如果找到的(i,j)不是(i,u),那么(i,j)一定可以和(i,v)拼成另一条更长路(j,v)
    (因为(i,j)>=(i,u)而等于正印证了上面讲到的多条直径),所以与假设(u,v)是最长路不符,那么猜想的方法成立

如果i不在最长路上呢?

  1. 首先\(i\)肯定可以和最长路上的一个点\(k\)(从\(i\)到最长路最先遇到的点)连通对吧
  2. 我们假设\(u\)是最长路距离\(k\)较远的一个末端,那么从\(i\)找出去的最长路一定会到\(u\)
  • 证明:如果(i,j)不是(i,u)(即(i,j)>(i,u)),那么(i,j)+(i,k)>(j,u),即我们找到了另一条(j,k)>(j,u),那么说明我们假设的直径(u,v)又可以被更长的(k,v)更新,假设不成立,那么猜想成立,证明完毕

总结一下

综上所述,两遍\(Dfs(Bfs)\)可以找到树的直径
而树的直径在很多图论题里面是很有用的,这种方法就保证了我们的时间复杂度O(n)
为我们其他计算提供了更优的复杂度空间。。。
\(yep\)

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Python求直径最小生成树的步骤: 1. 首先,需要使用Kruskal算法来生成最小生成树。 2. 然后,需要对最小生成树进行遍历,找到最长的边,即为直径。 3. 最后,输出直径和最小生成树。 下面是Python代码示例: ```python # 定义边的类 class Edge: def __init__(self, u, v, w): self.u = u self.v = v self.w = w # 定义并查集类 class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.rank = [0] * n def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): px, py = self.find(x), self.find(y) if px == py: return False if self.rank[px] < self.rank[py]: px, py = py, px self.parent[py] = px if self.rank[px] == self.rank[py]: self.rank[px] += 1 return True # 定义Kruskal算法函数 def kruskal(n, edges): uf = UnionFind(n) edges.sort(key=lambda e: e.w) res = [] for e in edges: if uf.union(e.u, e.v): res.append(e) if len(res) == n - 1: break return res # 定义求直径函数 def diameter(n, edges): mst = kruskal(n, edges) dist = [-1] * n dist[mst[0].u] = 0 q = [mst[0].u] while q: u = q.pop(0) for e in mst: if e.u == u and dist[e.v] == -1: dist[e.v] = dist[u] + e.w q.append(e.v) elif e.v == u and dist[e.u] == -1: dist[e.u] = dist[u] + e.w q.append(e.u) u = max(range(n), key=lambda i: dist[i]) dist = [-1] * n dist[u] = 0 q = [u] while q: u = q.pop(0) for e in mst: if e.u == u and dist[e.v] == -1: dist[e.v] = dist[u] + e.w q.append(e.v) elif e.v == u and dist[e.u] == -1: dist[e.u] = dist[u] + e.w q.append(e.u) return max(dist) # 测试 n = 5 edges = [Edge(0, 1, 1), Edge(0, 2, 2), Edge(0, 3, 3), Edge(0, 4, 4), Edge(1, 2, 5), Edge(1, 3, 6), Edge(1, 4, 7), Edge(2, 3, 8), Edge(2, 4, 9), Edge(3, 4, 10)] print("最小生成树:", kruskal(n, edges)) print("直径:", diameter(n, edges)) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值