動態連通性解決
阿新 • • 發佈:2018-12-17
動態連通性介紹
假設p,q是相連的,相連具有等價關係
- 自反性:p和p也是相連的
- 對稱性:如果p和q是相連的,那麼q和p也是相連的
- 傳遞性:如果p和q相連,q和r相連,那麼p和r也是相連的
動態連通性究竟是什麼呢,看圖就會明白了
你能隨便找兩點就能確定它是連通的嗎,還有這裡一共有幾條連通路徑?
union-find和quick-union通用程式碼
public class Union {
private int []id;//分量id
private int count;//分量數量
public Union(int N) {
count=N;
id=new int[N];
//按順序初始化
for(int i=0;i<N;i++)
id[i]=i;
}
//返回連通分量個數
public int getCount(){
return count;
}
//判斷是否連通
public boolean isConnected(int p,int q){
return find(p)==find(q);
}
//這兩個是重點
//private int find(int n){}
//public int join(int p,int q)
}
union-find
要點
- 該演算法相當於用標記法,將在同一分量上的點全都標記成一個值
- 當要是p,q兩點是要相連的,那麼id[p],id[q]是相等的
缺點
-
find操作雖然很快,但是對於join操作,每一次連通都需要掃描一次陣列
-
這種演算法是平方級別的,假如最後只得到一個連通分量,那麼至少要呼叫N-1次join,至少訪問陣列為
(N+3)(N-1)~N²,然而大型應用無法應用的
程式碼
//找到該點的分量值
private int find(int n){
return id[n];
}
//連通兩個點
public void join(int p,int q){
int pId=find(p);
int qId=find(q);
//如果已經相連就不做處理
if (pId==qId)return;
//將p的分量重新接到q的名稱
for(int i=0;i<id.length;i++)
if(id[i]==pId)id[i]=qId;
count--;
}
quick-union
要點
- id數組裡儲存的是指向上一結點
- 如果p,q是連通的,那麼他們會指向統一根結點
- 根結點總是指向自己的,當然初始化的時候所有結點都是自己的
缺點
- 雖然它比上一個union-find演算法快,但是不它不穩定
- 當在最壞的情況下,就是恰好每次將深的樹,連線在了淺的樹訪問陣列的總次數為3+5+7···+(2*N-1)~N²
程式碼
private int find(int n){
while(n!=id[n])n=id[n];
return n;
}
//連通兩個點
public void join(int p,int q){
//找到根結點
int pId=find(p);
int qId=find(q);
//如果已經相連就不做處理
if(pId==qId)return;
//id[pId]根接到qId的根上
id[pId]=qId;
count--;
}
加權quick-union
要點
- 就是在quick-union基礎上在每個結點上加上孩子結點的數量
- 我們總是將小樹連線到大樹上
程式碼
public class WeightUnion{
private int []id;//分量id
private int []sz;//分量大小
private int count;//分量數量
public Union(int N) {
count=N;
id=new int[N];
//按順序初始化
for(int i=0;i<N;i++){
id[i]=i;
sz[i]=1;
}
}
//返回連通分量個數
public int getCount(){
return count;
}
//判斷是否連通
public boolean isConnected(int p,int q){
return find(p)==find(q);
}
private int find(int n){
while(n!=id[n])n=id[n];
return n;
}
//連通兩個點
public void join(int p,int q){
//找到根結點
int pId=find(p);
int qId=find(q);
//如果已經相連就不做處理
if(pId==qId)return;
//p為小樹,q為大樹
if(sz[pId]<sz[qId]){
//小樹連線到大樹
id[pId]=qId;
//大樹更新分量大小
sz[qId]+=sz[pId];
}else{
//q為小樹,p為大樹
id[qId]=pId;
sz[pId]+=sz[qId];
}
count--;
}
}