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

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代码

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]=x
            self.rootSizes[x]=1
            self.setCnt+=1
    
    def find(self,x):
        root=x
        while root != self.roots[root]:
            root=self.roots[root]
        # 优化:压缩路径
        while x!=root:
            temp=self.roots[x]
            self.roots[x]=root
            x=temp
        return root
    
    def 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]=rooty
                self.rootSizes[rooty]+=self.rootSizes[rootx]
            else:
                self.roots[rooty]=rootx
                self.rootSizes[rootx]+=self.rootSizes[rooty]
            self.setCnt-=1


from 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.length
        self.low=[-1]*self.length
        self.bridges=[]
        self.timestamp=0
    
    def 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.timestamp
        self.timestamp+=1
        for 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=0
        while i<edgesLength:
            # 获取j的值
            j=i
            while j<edgesLength and edges[i][2]==edges[j][2]:
                j+=1
            # 将各个点按并查集分成m个连通分量,每个连通分量为新图compG中的一个节点,并给每个连通分量初始化需要i
            node2CompId={}
            compCnt=0
            for 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]=compCnt
                        compCnt+=1
                    if node2Root not in node2CompId:
                        node2CompId[node2Root]=compCnt
                        compCnt+=1
                else:
                    labels[edgeId]=0    # 改变既不是关键边,也不是伪关键边
            # print(node2CompId)
            # 构建邻接表形式的compG和对应的边的id
            compG=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)
            # 将桥标记为1
            for 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)
            # 更新i
            i=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.执行结果

相关推荐
低调包含1 分钟前
CRC校验
算法
ya888g32 分钟前
信息学奥赛复赛复习19-CSP-J2023-02公路-贪心算法、向上取整、向下取整
c++·算法
夜雨翦春韭41 分钟前
【代码随想录Day58】图论Part09
java·开发语言·数据结构·算法·leetcode·图论
纪怽ぅ1 小时前
LSTM——长短期记忆神经网络
python·深度学习·神经网络·算法·机器学习·lstm
yannan201903131 小时前
【算法】(Python)回溯算法
python·算法
无职转生真好看1 小时前
数据结构:利用队列的基本操作,模拟病人到医院排队看病过程。
数据结构·c++·算法
心软且酷丶1 小时前
leetcode:面试题 05.07. 配对交换(python3解法)
python·算法·leetcode
跃跃欲试-迪之2 小时前
构建您自己的 RAG 应用程序:使用 Ollama、Python 和 ChromaDB 在本地设置 LLM 的分步指南
人工智能·python·算法
给自己做减法2 小时前
有趣的回溯法寻求最优解
算法·回溯算法
WangMing_X2 小时前
C# 单个函数实现各进制数间转换
java·开发语言·算法·c#·winform·软件