并查集应用:判圈JAVA C++

并查集应用:判圈

Description

第一行输入正整数n,m,q表示一个有n个点m条边的无向图。q表示有q次询问。

接下来m行有m条边。每行两个u,v属于[1,n]范围的正整数,表示u,v之间有边。

接下来q行,每行两个点u,v,属于[1,n]。

如果(u,v)这条边已经存在或者如果加入这条边后会产生新的环,则输出一行YES,否则输出NO

Sample Input 1 

5 3 3
1 2
2 3
3 1
1 2
3 4
4 5

Sample Output 1

YES
NO
NO

来源:oj作业三

这里进行的q次判断不需要把边加进去

java的代码进行了路径压缩和树高优化,查询更快。两种语言的代码差不多,可以自己改

 C++代码 (没有路径压缩和树高优化)

#include<bits/stdc++.h>
using namespace std;
int n,m,q;//n个顶点m条边的无向图,q次询问
int u,v;
int parent[1000];
set <pair<int,int>> edge;

// 查找
int find(int i){
    while(parent[i]!=i){
        i=parent[i];
    }
    return i;//返回父节点

    /* 路径压缩,不压缩有些代码比如kruskal会超时
    int find(int i){
        if (parent[i] != i) {
        parent[i] = find(parent[i]); 
        }
        return parent[i];
    }    
    */
}

//合并结点
void unionNode(int i,int j){
    //分别查找两个结点ij的父节点,不同则合并
    int r1=find(i);
    int r2=find(j);
    if(r1!=r2){
        parent[r2]=r1;//把r1作为r2的父节点,也可以反过来
    }
}

int main(){
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)//初始每个结点父节点都是自己
    parent[i]=i;

    for(int i=1;i<=m;i++){
        cin>>u>>v;
        //无向图
        edge.insert({u,v});
        edge.insert({v,u});
        unionNode(u,v);//加入并查集
    }

    for(int i=1;i<=q;i++){
        cin>>u>>v;

        if(edge.count({u,v})>0){
            cout<<"YES"<<endl;//已有这条边
        }
        else if(find(u)==find(v)){
            cout<<"YES"<<endl;//会形成环
        }
        else{
            cout<<"NO"<<endl;
        } 
    }

    return 0;
}

java代码(有树高以及路径路径压缩的优化)


import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        int q = sc.nextInt();
        
        UnionFind unionfind= new UnionFind(n);
        Set<String> edge= new HashSet<>();//hashSet存放边集
        
        for(int i=1;i<=m;i++) {
        	int u=sc.nextInt();
        	int v=sc.nextInt();
        	edge.add(u+","+v);
        	edge.add(v+","+u);//无向图记得反向
        	unionfind.union(u, v);//加入并查集
        }
        
        for(int i=1;i<=q;i++) {
        	int u=sc.nextInt();
        	int v=sc.nextInt();
        	if(edge.contains(u+","+v)) {
        		System.out.println("YES");
        	}
        	else if(unionfind.find(u)==unionfind.find(v)) {
        		System.out.println("YES");
        	}
        	else {
        		System.out.println("NO");
        	}
        }
       
    }
}

//并查集
class UnionFind{
	private int[] parent;
	private int[] rank;//树高,用于减少递归次数
	
	public UnionFind(int n) {
		parent = new int[n+1];
		rank = new int[n+1];
		for(int i=1;i<=n;i++) {
			parent[i]=i;//初始每个节点的祖先节点都是自己
			rank[i]=1;//初始树高为1
		}
	}
	
	//查通过递归父节点,找到祖先节点,如果祖先节点是自己,说明是环
	public int find(int u) {
		if(parent[u]!=u) {
			parent[u]=find(parent[u]);
		}
		return parent[u];
		}

	public void union(int u,int v) {
		int rootU=find(u);
		int rootV=find(v);
		//为了维持树高,哪里的树更矮,就作为父节点
		if(rootU!=rootV) {
			if(rank[rootU]>rank[rootV]) {
				parent[rootV]=rootU;
			}else if(rank[rootU]<rank[rootV]) {
				parent[rootU]=rootV;
			}else {
				parent[rootV]=rootU;
				rank[rootU]++;
			}
		}
	}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值