题目
https://leetcode-cn.com/problems/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree/
方法一:枚举 + 最小生成树判定
知识
1.克鲁斯卡尔算法:在E中选择代价最小的边,若该边依附的顶点分别在T中不同的连通分量上,则将此边加入到T中;否则,舍去此边而选择下一条代价最小的边。依此类推,直至T中所有顶点构成一个连通分量为止。
2.连通分量:
分析
1.首先对原图执行 Kruskal 算法,得到最小生成树的权值value。
2.,枚举每条边,判断是否关键边。需要注意的是,关键边也满足伪关键边对应的性质。所有先判断关键边,如果不是才去判断伪关键边。
3.如果不是关键边,判断是否伪关键边。
代码
class Solution {
public List<List<Integer>> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) {
int m=edges.length;
int[][] newEdges=new int[m][4];
for(int i=0;i<m;i++){
for(int j=0;j<3;j++){
newEdges[i][j]=edges[i][j];
}
newEdges[i][3]=i;//边的序号
}
Arrays.sort(newEdges,(u,v)->(u[2]-v[2]));//按边的权值升序
//求最小生成树的权值value
UnionFind uf=new UnionFind(n);
int value=0;
for(int i=0;i<m;i++){
if(uf.union(newEdges[i][0],newEdges[i][1])){
value+=newEdges[i][2];//加上这条边的权值
}
}
//结果
List<List<Integer>> res=new ArrayList<>();
res.add(new ArrayList<Integer>());
res.add(new ArrayList<Integer>());
for(int i=0;i<m;i++){
//判断是否关键边
UnionFind ud=new UnionFind(n);
int v=0;
for(int j=0;j<m;j++){
//i!=j:不要i这条边
//加入newEdge[j]这条边
if(i!=j&&ud.union(newEdges[j][0],newEdges[j][1])){
v+=newEdges[j][2];
}
}
//不连通 或 连通但v变大
//i是关键边
if(ud.setCount!=1||(ud.setCount==1&&v>value)){
res.get(0).add(newEdges[i][3]);
continue;//不需要判断非关键边了
}
//判断是否伪关键边
ud=new UnionFind(n);
//在计算最小生成树的过程中,最先考虑这条边,
//即最先将这条边的两个端点在并查集中合并
ud.union(newEdges[i][0],newEdges[i][1]);
v=newEdges[i][2];//初始总权值:这条边的权值
for(int j=0;j<m;j++){
//除了i这条边
//加入其他边
if(i!=j&&ud.union(newEdges[j][0],newEdges[j][1])){
v+=newEdges[j][2];
}
}
if(v==value){
res.get(1).add(newEdges[i][3]);
}
}
return res;
}
}
// 并查集模板
class UnionFind{
public int[] parent;
public int[] size;
public int setCount;//当前连通分量数目
public UnionFind(int n){
this.parent=new int[n];
this.size=new int[n];
Arrays.fill(size,1);
this.setCount=n;
for(int i=0;i<n;i++){
parent[i]=i;
}
}
public int find(int index){
if(parent[index]!=index){
parent[index]=find(parent[index]);
}
return parent[index];
}
public boolean union(int index1,int index2){
int a=find(index1);
int b=find(index2);
if(a==b) return false;
if(size[a]<size[b]){
int tmp=a;
a=b;
b=tmp;
}
parent[b]=a;
size[a]+=size[b];
setCount--;
return true;
}
public boolean connected(int index1,int index2){
int a=find(index1);
int b=find(index2);
if(a==b)
return true;
return false;
}
}