1. 程式人生 > >動態連通性解決

動態連通性解決

動態連通性介紹

假設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--;
	}
}