对于一个无向图的连通性的判断,我们可以通过读入的边对得出邻接矩阵,然后可以采用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);
}