1.基本
注意:
1.合并函数记得不要把roota和a写混,合并函数中的更新操作总是对根节点更新,对子节点的更新是在find函数中操作的。
按秩归并+路径压缩 板子
public static int maxn=30010,maxm=550,n,m,rank[]=new int[maxn],root[]=new int[maxn];
public static void init() {
for(int i=0;i<n;i++) {
rank[i]=0;
root[i]=i;
}
}
public static int find(int x) {
if(root[x]!=x) {
root[x]=find(root[x]);
}
return root[x];
}
public static boolean union(int x,int y) {
int a=find(x);
int b=find(y);
if(a!=b) {
if(rank[a]>rank[b]) {
root[b]=a;
}else if(rank[a]<rank[b]) {
root[a]=b;
}else {
root[a]=b;
rank[b]++;
}
return true;
}else return false;
}
2.扩展
1.种类并查集
在进行关系域更新时,可以采用向量法或分析法。
注意:
1.关系域的式子出现在find()函数、合并不同根节点、判断同根点之间关系(这个不用更新)三个地方。
2.find()函数的任务除路径压缩外又多了一个更新关系。
3.使用向量法时,注意在出现减号的式子里多加几个(根据减号数量判断)mod,以抵消掉结果的负号。
向量法伪板子
public class Main {
public static int maxn=10010,rank[]=new int[maxn],root[]=new int[maxn];
public static void init() {
for(int i=0;i<maxn;i++) {
root[i]=i;
rank[i]=0;
}
}
public static int find(int x) {
if(x!=root[x]) {
int f=root[x];
root[x]=find(root[x]);
rank[x]=(rank[x]+rank[f])%2;
}
return root[x];
}
public static void main(String args[]) {
Scanner sc=new Scanner (System.in);
int n=sc.nextInt();
int ans=0;
init();
int m=sc.nextInt();
for(int i=0;i<m;i++) {
int a=sc.nextInt();
int b=sc.nextInt();
b++;
int v;
if(sc.next().charAt(0)=='e') {
v=0;
}else {
v=1;
}
int roota=find(a);
int rootb=find(b);
if(roota!=rootb) {
root[roota]=rootb;
rank[roota]=(2+v+rank[b]-rank[a])%2;
}else {
if(v!=(2+rank[a]-rank[b])%2) {
ans=i;
break;
}
}
}
System.out.println(ans);
}
}
2.判树
什么样的图是一棵树?
首先定义根节点为入度为0的点.
无向图为树:
1.无环
2.只有一个根节点
有向图为树:
1.只有一个根节点
2.除了这个根节点外的所有点入度为1
如果一个有向图中出现了入度不为1的点,那么指向这个点的点不是异根结点,就是同根结点.如果是前者,则此图有多个根节点;如果是后者,则此图存在环.
因此,有向图判树也可以归结为:
1.只有一个根节点
2.无环
注意无论有向无向不必在意指向问题,因为谁做根节点都可以…
并查集的合并操作很方便判断是否同根.使用并查集判树时,
1.如果合并时出现两个同根结点,则存在环.
2.如果find(x)的值有不同,则存在多个根节点.
有向无向代码完全相同
public class test {
public static int maxn=1000010,root[]=new int[maxn];
public static boolean vis1[]=new boolean[maxn];
public static int find(int x) {
if(x!=root[x]) {
root[x]=find(root[x]);
}
return root[x];
}
public static boolean unite(int a,int b) {
int roota=find(a);
int rootb=find(b);
if(roota!=rootb) {
root[roota]=rootb;
return true;
}else {
return false;
}
}
public static boolean single_root() {
for(int i=0;i<maxn;i++) {
if(vis1[i]) {
int f=find(i);
//System.out.println(f);
for(int j=0;j<maxn;j++) {
if(vis1[j]&&find(j)!=f) {
return false;
}
}
}
}
return true;
}
public static void main(String args[]) {
Scanner sc=new Scanner(System.in);
int a;
int b;
int ccase=0;
boolean tree=true;
for(int i=0;i<maxn;i++) {
vis1[i]=false;//是否考虑,有的数字没有出现在图上,就不考虑
root[i]=i;
}
while(sc.hasNext()) {
a=sc.nextInt();
b=sc.nextInt();
if(a<0) {
break;
}
if(a==0||b==0) {
ccase++;
if(tree&&single_root()) {
System.out.println("Yes");
}else {
System.out.println("No");
}
tree=true;
for(int i=0;i<maxn;i++) {
vis1[i]=false;//是否考虑
root[i]=i;
}
continue;
}
vis1[a]=true;
vis1[b]=true;
if(!unite(b,a)) {
tree=false;//tree表示是否有环
}
}
}
}
p.s.只有一个根节点可以说成是此图连通.