Leetcode 1489. 找到最小生成树里的关键边和伪关键边

news/2024/10/15 18:09:05

1.题目基本信息

1.1.题目描述

给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1 ,同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti] 表示在 fromi 和 toi 节点之间有一条带权无向边。最小生成树 (MST) 是给定图中边的一个子集,它连接了所有节点且没有环,而且这些边的权值和最小。

请你找到给定图中最小生成树的所有关键边和伪关键边。如果从图中删去某条边,会导致最小生成树的权值和增加,那么我们就说它是一条关键边。伪关键边则是可能会出现在某些最小生成树中但不会出现在所有最小生成树中的边。

请注意,你可以分别以任意顺序返回关键边的下标和伪关键边的下标。

1.2.题目地址

https://leetcode.cn/problems/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree/description/

2.解题方法

2.1.解题思路

无向图的连通性质:取权值为w的边,让小于w的边构成一个一个的连通分量comps,将每个连通分量comp作为一个节点,将w大小的节点与comps这些节点重新构建一个图,记为compG。

compG中的桥即为关键边;w大小的边的两端如果在同一个连通分量内,则既不是关键边也不是伪关键边;余下的都是伪关键边。

2.2.解题步骤

解题步骤请看代码注释

3.解题代码

Python代码

# # ==> 并查集模板(附优化)
class UnionFind():def __init__(self):self.roots={}self.setCnt=0   # 连通分量的个数# Union优化:存储根节点主导的集合的总节点数self.rootSizes={}def add(self,x):if x not in self.roots:self.roots[x]=xself.rootSizes[x]=1self.setCnt+=1def find(self,x):root=xwhile root != self.roots[root]:root=self.roots[root]# 优化:压缩路径while x!=root:temp=self.roots[x]self.roots[x]=rootx=tempreturn rootdef union(self,x,y):rootx,rooty=self.find(x),self.find(y)if rootx!=rooty:# 优化:小树合并到大树上if self.rootSizes[rootx]<self.rootSizes[rooty]:self.roots[rootx]=rootyself.rootSizes[rooty]+=self.rootSizes[rootx]else:self.roots[rooty]=rootxself.rootSizes[rootx]+=self.rootSizes[rooty]self.setCnt-=1from typing import List
class UndirectedGraphTarjan2():def __init__(self,graph:List[int:List[int]],graphEdges:List[int:List[int]]):self.length=len(graph)self.graph=graph    # 邻接表self.graphEdges=graphEdges  # 邻接表同结构的边self.dfn=[-1]*self.lengthself.low=[-1]*self.lengthself.bridges=[]self.timestamp=0def tarjan(self):for i in range(self.length):if self.graph[i]!=-1:self.tarjanDfs(i,-1)def tarjanDfs(self,node:int,fatherEdge:int):self.dfn[node]=self.low[node]=self.timestampself.timestamp+=1for i in range(len(self.graph[node])):subNode,edge=self.graph[node][i],self.graphEdges[node][i]   # 这里边是node指向subNode的边if self.dfn[subNode]==-1:self.tarjanDfs(subNode,edge)self.low[node]=min(self.low[node],self.low[subNode])if self.dfn[node]<self.low[subNode]:self.bridges.append(edge)elif edge!=fatherEdge:self.low[node]=min(self.low[node],self.low[subNode])from collections import defaultdict
class Solution:def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]:edgesLength=len(edges)# 将所有的边编号并获取点->边id的映射for i in range(edgesLength):edges[i].append(i)# 将所有的边按权值从小到大进行排序edges.sort(key=lambda x:x[2])# 构建所有点的并查集(不用进行连接)uf=UnionFind()for i in range(n):uf.add(i)# 初始化所有边的标签为-1(1为关键边,0则两者都不是,最后还是-1的为伪关键边)labels=[-1]*edgesLength# 遍历所有的相同权值的边组edges[i,j)i=0while i<edgesLength:# 获取j的值j=iwhile j<edgesLength and edges[i][2]==edges[j][2]:j+=1# 将各个点按并查集分成m个连通分量,每个连通分量为新图compG中的一个节点,并给每个连通分量初始化需要inode2CompId={}compCnt=0for k in range(i,j):node1,node2,weight,edgeId=edges[k]node1Root,node2Root=uf.find(node1),uf.find(node2)if node1Root!=node2Root:if node1Root not in node2CompId:node2CompId[node1Root]=compCntcompCnt+=1if node2Root not in node2CompId:node2CompId[node2Root]=compCntcompCnt+=1else:labels[edgeId]=0    # 改变既不是关键边,也不是伪关键边# print(node2CompId)# 构建邻接表形式的compG和对应的边的idcompG=defaultdict(list)compGEids=defaultdict(list)for k in range(i,j):node1,node2,weight,edgeId=edges[k]node1Root,node2Root=uf.find(node1),uf.find(node2)if node1Root!=node2Root:compId1,compId2=node2CompId[node1Root],node2CompId[node2Root]compG[compId1].append(compId2)compGEids[compId1].append(edgeId)compG[compId2].append(compId1)compGEids[compId2].append(edgeId)# print(compCnt, compG, compGEids)ugt2=UndirectedGraphTarjan2(compG, compGEids)ugt2.tarjan()bridges=ugt2.bridges# print("bridges",bridges)# 将桥标记为1for bridge in bridges:labels[bridge]=1# 遍历[i,j)之间的边。如果该边是compG的桥,则该边是关键边;如果该边首尾连接同一个连通分量节点,则既不是关键边,也不是伪关键边;for k in range(i,j):node1,node2,weight,edgeId=edges[k]uf.union(node1,node2)# 更新ii=j# 标记完成后,剩下的没有标记的边即为伪关键边# 返回结果# print(labels)result=[[],[]]for i in range(edgesLength):if labels[i]==1:result[0].append(i)elif labels[i]==-1:result[1].append(i)return result

4.执行结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/71917.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

sunoai怎么下载做好的音乐(sunoai下载音乐教程)

下载Sunoai制作的音乐需首先访问官网并登录账号。在“Create”板块输入歌曲描述并生成,完成后点击分享按钮获取下载链接。若需要,可部署自己的下载服务器,通过访问弹性公网IP下载。最后,将链接粘贴到下载站输入框并点击下载。Sunoai音乐下载教程 访问Sunoai官网:首先,您需…

通过 chatgpt 修复org.springframework:spring-webmvc 安全漏洞过程记录(chatgpt有时候会乱说或者提不出最优方案)

1,首先我把这个安全漏洞的trivy完整描述send给了chatgpt并且随后把我的pom.xml也完整的send给了它。 chatgpt给出的答案还算比较靠谱。图一 图二 图三 图四 2,根据chatgpt的回复,我把<parent> <groupId>org.springframework.boot</groupId> <a…

AI网关在应用集成中起到什么作用?

现在,国内外几乎每个SaaS服务商都找到办法把大型语言模型(LLM)集成到自己的产品里。印证了那句话“每款SaaS都值得用AI重做一遍”我们暂且不讨论是否值得用AI重做,但是增加AI的功能,确实能让产品有更多的卖点。 通过整合各个软件应用中的数据和工作流程,组织能够实现应用…

例2.44求方程

from scipy.optimize import fsolve,root fx=lambda x: x**980-5.01*x**979+7.39*x**978-3.388*x*977-x**3+5.01*x**2-7.398*x+3.388 #函数被调用4000次 x1=fsolve(fx,1.5,maxfev=4000) x2=root(fx,1.5) print(x1,\n,----------) print(x2) print("学号:3008") 结果…

JVM——DAY1

定义 Java Virtual Machine: java二进制字节码的运行环境 好处一次编写,到处运行 自动内存管理,垃圾回收功能 数组下标越界检查 多态比较 jdk,jre,jvm学习jvm有什么用面试 理解底层实现原理 中高级程序员的必备技能学习路线参考学习视频为黑马程序员JVM课程地址:https://www…

例2.40DataFrame数据的拆分,合并和分组计算示例

import pandas as pd import numpy as np d = pd.DataFrame(np.random.randint(1,6,(10,4)),columns = list("ABCD")) d1 = d[:4] d2 = d[4:] dd= pd.concat([d1,d2]) s1 = d.groupby(A).mean() s2 = d.groupby(A).apply(sum)print("学号:3008") 结果如下…

例2.41DataFrame数据操作示例

import pandas as pd import numpy as np a = pd.DataFrame(np.random.randint(1,6,(5,3)),index=[a,b,c,d,e],columns=[one,two,three])a.loc[a,one] = np.nan b = a.iloc[1:3,0:2].values a[four] = bar a2 = a.reindex([a,b,c,d,e,f]) a3 = a2.dropna()print("学号:30…

例2.36 求下列矩阵的特征值以及特征向量

import numpy as np a = np.eye(4) b = np.rot90(a) c,d = np.linalg.eig(b) print("特征值:",c) print("特征向量:\n",d)print("学号:3008") 结果如下