1. 程式人生 > >無向圖的連通性的判斷

無向圖的連通性的判斷

        對於一個無向圖的連通性的判斷,我們可以通過讀入的邊對得出鄰接矩陣,然後可以採用Warshall演算法得到可達矩陣,那麼就可以很簡單的判斷圖的連通性,只要所有的點之間都是相互可達的,那麼圖就是連通的,反之則不連通。

        不過本文想介紹的不是該種方法,下面我們將用Union-find演算法來進行圖的連通性的判斷。該演算法的數學基礎思想是:

我們根據讀入的邊對進行集合的劃分,讀入的邊對證明是相連的,那麼我們就劃分到一個集合中,然後讀入新的邊對就劃分到新的集合中,一旦讀入的邊對(兩個頂點)都在同一個集合中出現過,那麼證明是相互連通的,如果讀入的邊對,兩個頂點是出現在兩個集合中,那麼說明這兩個集合至少通過該邊是可以相連的,那麼集合進行合併。依次類推.......

那麼剩下的就是資料結構的選取:
我們可以讓每次讀入邊的頂點對,然後設定一個十字連結串列,
head--->[0]--->[9]--->[7]
              |          |
             \|/        \|/
            [5]       [8]
             |
            \|/
           [1]
該連結串列的操作是,我們根據讀邊建立方法讀取的邊,每次讀入的邊我們是視為連線的,那麼就放入一個集合(用一條豎著的連結串列表示),然後每次讀入新的頂點對的時候就對連結串列進行比較,如果頂點分別出現在兩豎著的連結串列中,那麼合併兩個連結串列。如果只有一個出現在連結串列中,那麼另外一個點就加入該豎著的連結串列。

上面的思路正確,但是資料結構存在很大的優化空間,我們可以採用一個數組來代替這個十字連結串列,讓陣列對應頂點,陣列中的數就是頂點的識別符號,連通的頂點就記錄相同的識別符號。然後遇見兩個連通分量被當前讀入的邊對連線上的情況,那麼我們就更新陣列就行。

我們可以繼續在上面的陣列的情況上繼續優化,我們可以採用森林的方法來記錄連通分量,連通的就是一棵樹,整個圖構成一個森林,當然我們還是可以用陣列來模擬森林,那麼每個陣列的空間就是記錄其父節點的編號,根節點記錄自己的編號,這樣的有點就是在兩個連通分量連線的時候我們不必重新整理陣列的該連通分量的所有數。

#include <stdio.h>
#include <stdlib.h>

typedef struct list{
	int number;           //節點編號
	struct list *next;
}ListNode;

struct adjNode{
	int VMess;            //節點編號
	ListNode *head;       //鄰接連結串列
};

//圖資料結構
typedef struct A{
	int V;                //頂點個數
	int E;                //邊個數
    struct adjNode *graphMess;  //圖的資訊
}Graph;

void addEdge(Graph graph,int V1,int V2);

//連結串列的插入(始終是尾插)
void Add(ListNode **head,int num)
{
	if(!(*head))
	{
		(*head) = (ListNode *)malloc(sizeof(ListNode));
		(*head)->number = num;
		(*head)->next = NULL;
	}
	else
	{
		ListNode *temp = *head;
		while(temp->next)
			temp = temp->next;
		ListNode *T = (ListNode *)malloc(sizeof(ListNode));
		T->number = num;
		T->next = NULL;
		temp->next = T;
	}
}

//暫時不做刪除
void Delete()
{

}

//圖的操作:
//初始化
void InitGraph(Graph *graph,int VSize)
{
	graph->V = VSize;
	graph->E = 0;
	graph->graphMess = (struct adjNode *)malloc(sizeof(struct adjNode)*VSize);
	//從0開始編號節點
	for(int i = 0;i < VSize;i++)
	{
		graph->graphMess[i].VMess = i;
		//初始化鄰接連結串列為空
		graph->graphMess[i].head = NULL;
	}
}

//圖的建立
Graph CreateGraph()
{
	Graph graph;
	int VSize;
	scanf("%d",&VSize);
	InitGraph(&graph,VSize);
	//邊數的讀入
	scanf("%d",&graph.E);
	int V1,V2;
	for(int i = 0;i < graph.E;i++)
	{
		scanf("%d%d",&V1,&V2);
		addEdge(graph,V1,V2);
	}
	return graph;
}


//新增邊,不考慮新加入節點
void addEdge(Graph graph,int V1,int V2)
{
	Add(&(graph.graphMess[V1].head),V2);
	Add(&(graph.graphMess[V2].head),V1);
	graph.E++;
}

//圖的展示
void Display(Graph graph)
{
	ListNode *temp;
	for(int i = 0;i < graph.V;i++)
	{
		temp = graph.graphMess[i].head;
		printf("%d:  ",i);
		while(temp->next)
		{
			printf("%d-->",temp->number);
			temp = temp->next;
		}
		printf("%d\n",temp->number);
	}//end of for
}


void main()
{
	Graph graph = CreateGraph();
	Display(graph);
}