java最簡單的並查集(不想交集合)以及杭電1272
阿新 • • 發佈:2019-01-22
並查集要有的一些屬性:value:表示當前值,指標:(不一定是指標)指向父節點。 還有一個屬性number:表示該樹存在的總個數。(也可以用深度表示)。我用小樹插在大樹上。
如果是普通數字表示的樹,可以簡化:
初始全部-1,-1表示指向自己,陣列的值表示指向。你可能會問那麼總數怎麼表示,很簡單,其實我們不需要知道所有節點的總數,只需要根節點的總數就可以了,正常情況下根節點的初始是-1,但是一旦插入資料,根節點+(-1),根節點用負數來表示這個總數,就不用另外開一個屬性來表示了。
總結來說:初始為-1,合併之後保留大的做父節點。小根指向父節點,值傳給父節點,父節點的值為總個數。
舉個例子:
1,3合併。1並在3三上,a[3]=a[1]+a[3].(a[3]的值為-1+(-1)),然後1和4合併。4樹插在(1,3)樹上,a(1,3樹根)+=a[4];其實就是-2-1=-3;然後a[4]指向(1,3)樹的根。當然,你要先構造查詢根的值,和查詢根的序號函式,也比較容易,下面就看程式碼:
public class DisjointSet {
static int tree[]=new int[100000];//假設有500個值
public DisjointSet() {set(this.tree);}
public DisjointSet(int tree[])
{
this.tree=tree;
set(this.tree);
}
public void set(int a[])//初始化所有都是-1 有兩個好處,這樣他們指向-1說明是自己,第二,-1代表當前森林有-(-1)個
{
int l=a.length;
for(int i=0;i<l;i++)
{
a[i]=-1;
}
}
public int search(int a)//返回頭節點的數值
{
if(tree[a]>0)//說明是子節點
{
return search(tree[a]);
}
else
return a;
}
public int value(int a)//返回a所在樹的大小(個數)
{
if (tree[a]>0)
{
return value(tree[a]);
}
else
return -tree[a];
}
public void union(int a,int b)//表示 a,b所在的樹合併
{
int a1=search(a);//a根
int b1=search(b);//b根
if(a1==b1) {System.out.println(a+"和"+b+"已經在一棵樹上");}
else {
if(tree[a1]<tree[b1])//這個是負數,為了簡單減少計算,不在呼叫value函式
{
tree[a1]+=tree[b1];//個數相加 注意是負數相加
tree[b1]=a1; //b樹成為a的子樹,直接指向a;
}
else
{
tree[b1]+=tree[a1];//個數相加 注意是負數相加
tree[a1]=b1; //b樹成為a的子樹,直接指向a;
}
}
}
public static void main(String[] args)
{
DisjointSet d=new DisjointSet();
d.union(1,2);
d.union(3,4);
d.union(5,6);
d.union(1,6);
d.union(22,24);
d.union(3,26);
d.union(36,24);
System.out.println(d.search(6)); //頭
System.out.println(d.value(6)); //大小
System.out.println(d.search(22)); //頭
System.out.println(d.value(22)); //大小
}
}
執行結果:
6
4
24
3
實戰一個題目,杭電1272小希的迷宮,簡單修改下程式碼:注意陣列大小為100001以為所以。
import java.util.Scanner;
public class 杭電oj1722union {
public static void main(String[] args)
{
DisjointSet j=new DisjointSet();
Scanner sc=new Scanner(System.in);
boolean bool=true;
int number=0;
int last=0;
while(sc.hasNext())
{
int a=sc.nextInt();
int b=sc.nextInt();
if(a==-1&&b==-1) {break;}
else if(a==0&&b==0)//輸出
{
if(number==0||bool&&j.value(last)==number)
{
System.out.println("Yes");
}
else
{
System.out.println("No");
}
j=new DisjointSet(); number=0;
last=0;bool=true;
sc.nextLine();
}
else
if(bool) {
if(j.value(a)==1) {number++;}
if(j.value(b)==1) {number++;}
bool=j.union(a, b);last=b;}
}
}
}
class DisjointSet {
static int tree[]=new int[100001];//假設有500個值
public DisjointSet() {set(this.tree);}
public DisjointSet(int tree[])
{
this.tree=tree;
set(this.tree);
}
public void set(int a[])//初始化所有都是-1 有兩個好處,這樣他們指向-1說明是自己,第二,-1代表當前森林有-(-1)個
{
int l=a.length;
for(int i=0;i<l;i++)
{
a[i]=-1;
}
}
public int search(int a)//返回頭節點的數值
{
if(tree[a]>0)//說明是子節點
{
return search(tree[a]);
}
else
return a;
}
public int value(int a)//返回a所在樹的大小(個數)
{
if(tree[a]>0)
{
return value(tree[a]);
}
else
return -tree[a];
}
public boolean union(int a,int b)//表示 a,b所在的樹合併
{
int a1=search(a);//a根
int b1=search(b);//b根
if(a1==b1) {return false;}
else {
if(tree[a1]<tree[b1])//這個是負數,為了簡單減少計算,不在呼叫value函式
{
tree[a1]+=tree[b1];//個數相加 注意是負數相加
tree[b1]=a1; //b樹成為a的子樹,直接指向a;
}
else
{
tree[b1]+=tree[a1];//個數相加 注意是負數相加
tree[a1]=b1; //b樹成為a的子樹,直接指向a;
}
}
return true;
}
}