首页 > 其他分享 >Leetcode 1489. 找到最小生成树里的关键边和伪关键边

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

时间:2024-10-15 18:01:13浏览次数:1  
标签:self 节点 edges 关键 1489 root Leetcode roots

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]=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.执行结果

在这里插入图片描述

标签:self,节点,edges,关键,1489,root,Leetcode,roots
From: https://www.cnblogs.com/geek0070/p/18468103

相关文章

  • sqlserver 里的UNION 关键字是啥含义
    在SQLServer中,UNION是一种用于合并两个或多个SELECT语句结果集的操作符。它允许你将来自不同表或相同表但基于不同条件的查询结果合并成一个单独的结果集。使用UNION时,需要注意以下几点:列数和数据类型:所有SELECT语句必须返回相同数量的列,并且相应列的数据类型必须兼......
  • leetcode 刷题day43动态规划Part12(115.不同的子序列、583. 两个字符串的删除操作、72.
    115.不同的子序列思路:这个题还是比较有难度的,问题s中有多少个t子序列可以转化为s字符串中有多少删除元素的方式,使s可以变成t。考虑动规五部曲。1、确定dp数组(dptable)以及下标的含义dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。2、确定递推公式......
  • leetcode 刷题day42动态规划Part11(1143.最长公共子序列、1035.不相交的线、53. 最大子
    1143.最长公共子序列思路:718.最长重复子数组两个数组对应相同且连续,所以递推公式是dp[i-1][j-1]+1。最长公共子序列不要求连续但要求相对顺序,差别主要在于递推公式。对于该题主要有两大情况:text1[i-1]与text2[j-1]相同,text1[i-1]与text2[j-1]不相同。如果te......
  • Leetcode刷题
    本题思路:采用线性枚举,遍历数组暴力解题分析:首先我们定义两个变量p和count,p用来记录0之前1的个数,例如在示例1中我们的p遍历完数组后的值先为2,遇到0断开,将p重新变为0,之后值为3。而count则记录最长1有几个,在第一次中p等于2,此时count也等于2,当p重新为0时,count还是等于2......
  • LeetCode刷题日记之回溯算法(四)
    目录前言非递减子序列全排列全排列II总结前言今天是学习回溯算法的第四天,我们继续来一起学习回溯算法蕴含的逻辑处理,希望博主记录的内容能够对大家有所帮助,一起加油吧朋友们!......
  • 淘宝商品关键词API接口:关键词数据智能分析
    淘宝商品关键词API接口是淘宝开放平台(TaobaoOpenPlatform,TOP)提供的一项服务,它允许第三方开发者通过编程方式访问淘宝的商品信息数据库。这个接口的主要功能是根据开发者提供的关键词,检索淘宝平台上的商品列表及相关信息。一、功能和特点数据检索:可以检索特定关键词下的......
  • [LeetCode] 662. 二叉树最大宽度
    题目描述:给你一棵二叉树的根节点 root ,返回树的 最大宽度 。树的 最大宽度 是所有层中最大的 宽度 。每一层的 宽度 被定义为该层最左和最右的非空节点(即,两个端点)之间的长度。将这个二叉树视作与满二叉树结构相同,两端点间会出现一些延伸到这一层的 null 节点,这......
  • 《C++内存对齐策略:提升性能的关键之路》
    在C++编程的广阔世界中,高效的内存对齐策略是一个至关重要却常常被忽视的主题。它不仅影响着程序的性能,还关系到内存的使用效率和稳定性。今天,我们就来深入探讨一下如何在C++中实现高效的内存对齐策略。一、为什么内存对齐如此重要?内存对齐在C++中具有重大意义。首先,它......
  • Leetcode_exercise_01
    题目两数之和枚举所有可能的两个不同的数字之和,与target做比较。哈希表查询//方法一:classSolution{public:vector<int>twoSum(vector<int>&nums,inttarget){intn=nums.size();for(inti=0;i<n;++i){......
  • (nice!!!)(LeetCode) 1884. 鸡蛋掉落-两枚鸡蛋(动态规划 dfs递归和递推 || 数学)
    题目:1884.鸡蛋掉落-两枚鸡蛋方法一:动态规划dp+递归dfs+记忆化搜索。时间复杂度0(n^2)。C++版本:classSolution{public: //状态sta[i]表示:i层找到f所需要的最小操作次数intsta[1010];inttwoEggDrop(intn){ //层数为0时,直接返回0if(n==0......