圖的鄰接矩陣表示法&鄰接表表示法

圖表示是一種多對多的關係的數據結構。因爲線性表表示的是一種一對一的關係的數據結構,樹表示的是一種一對多的數據結構,所以圖把線性表和樹都包含在內。圖由一個非空的有限頂點集合和一個有限邊集合組成。當我們描述圖的時候,一定要包含以下兩個元素:

1、一組頂點:例如用Vertex表示頂點的集合。

2、一組邊:用Edge表示邊的集合,一條邊是一組頂點對。

      •無向邊 :(ij)∈EdgeijVertex。即雙向的,即可從i走到j,也可以從j走到i

•有向邊 :< ij >EdgeijVertex。即單向的(單行線),只可從i走到j或從j走到i

•圖不考慮重邊和自己連自己的情況。

初始化一個圖是,可以一條邊也沒有,但是不能一個頂點也沒有。

那麼怎麼用代碼表示一個圖?

第一種方法是鄰接矩陣表示法:即用矩陣,一個二維數組來表示一個圖。

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

#define MaxVertex 100  /*最大頂點數*/
#define INFINITY 65535 /*雙字節無符號整數的最大值*/
typedef int Vertex; /*二維數組的下標表示頂點,爲整型*/
typedef int WeightType; /*邊的權值的類型*/ 
typedef char DataType; /*頂點存儲的數據類型爲字符型*/

/*圖的數據結構*/
typedef struct GraphNode *PtrlGraphNode;
struct GraphNode {
	int numv; /*頂點數*/ 
	int nume; /*邊數*/ 
	WeightType wt[MaxVertex][MaxVertex]; /*鄰接矩陣*/
	DataType Data[MaxVertex]; /*存放頂點的數據*/
	/*很多情況下,頂點無數據,此時Data[]可以不用*/
};
typedef PtrlGraphNode Graph;/*用鄰接矩陣存儲的圖*/

用一個二維數組來表示圖中一組頂點對的關係,還有一個一維數組來存放頂點的數據。

/*邊的數據結構*/
typedef struct EdgeNode *PtrlEdgeNode;
struct EdgeNode {
	Vertex V1, V2; /*有向邊<V1, V2>*//*無向邊(V1, V2)*/
	WeightType weight; /*權重*/
};
typedef PtrlEdgeNode Edge;

邊的數據結構,一條邊由一組頂點對和權重表示(即頂點對的關係)。

表示圖的各個數據結構都建立好後,就開始建立一個圖,思路是先創建一個有全部頂點但沒有邊的圖,在逐條插入邊。

/*建立一個有VertexNum個頂點但沒有邊的空圖*/
Graph CreatGraph(int VertexNum) 
{
	Vertex i,j;
	Graph MyGraph;
	
	MyGraph=(Graph)malloc(sizeof(struct GraphNode));/*申請空間建立圖*/
	MyGraph->numv=VertexNum;
	MyGraph->nume=0; //初始化邊數爲0; 
	
	/*對二維數組矩陣初始化*/
	for (i=0;i<MyGraph->numv;i++) {
		for (j=0;j<MyGraph->numv;j++) {
			MyGraph->wt[i][j]=INFINITY;
		}
	}
	return MyGraph;
}

CreatGraph函數中輸入一個值來創建有Vertexnum個結點的圖,然後初始化二維數組中的元素爲無窮(或0)。

/*插入邊*/
void InsertEdge(Graph MyGraph, Edge MyEdge) 
{
	/*插入邊<V1, V2>*/
	MyGraph->wt[MyEdge->V1][MyEdge->V2]=MyEdge->weight;
	
	/*如果是無向圖,還要加上插入邊<V2, V1>*/
	MyGraph->wt[MyEdge->V2][MyEdge->V1]=MyEdge->weight;
}

然後插入邊,把邊的V1V2,也就是兩個頂點(對應二維數組的兩個下標),以及權值插入到對應二維數組的位置。

Graph BuildGraph() 
{
	Graph MyGraph; /*建圖*/
	Edge MyEdge; /*建邊*/ 
	Vertex v;
	int VertexNum,i;
	
	printf("請輸入頂點個數");
	scanf("%d", &VertexNum); 
	MyGraph=CreatGraph(VertexNum);/*建立一個有numv個頂點,沒有邊的圖*/
	
	printf("請輸入邊數:");
	scanf("%d", &MyGraph->nume);   /* 讀入邊數 */
	MyEdge=(Edge)malloc(sizeof(struct EdgeNode));/*建立邊結點*/
	/*插入邊:格式爲:"起點->終點->權重"。插入到鄰接矩陣*/ 
	for (i=0;i<MyGraph->nume;i++) {
		scanf("%d %d %d",&MyEdge->V1, &MyEdge->V2, &MyEdge->weight);
		InsertEdge(MyGraph, MyEdge);
	}
	
	/*如果頂點有數據,就讀入數據*/
	for (i=0;i<MyGraph->numv;i++) {
		scanf("%c", &MyGraph->Data[i]);
	}
	
	return MyGraph;
}

Graph[ n ][ n ]二維數組中,用n個頂點從0n-1編號。如果一組頂點對<  ij  >是圖Graph中的邊的話,就令Graph[ i ][ j ]=1,否則等於0

用鄰接矩陣表示法表示出來的矩陣圖,有以下特點:

1、矩陣上的對角線全都是0。因爲圖中的頂點對不會出現自己連自己的情況,所以對於矩陣圖中Graph[ n ][ n ]一定是等於0

2、鄰接矩陣表示法表示出來的矩陣無向圖,一定是沿對角線對稱的,也就是說一定是一個對稱矩陣。這是因爲,假設圖中結點12是有一條邊的,所以21之間也是有一條邊的,所以Graph[ 1 ][ 2 ]Graph[ 2 ][ 1 ]一定是相等都等於1的。


用矩陣的方式,好處是容易檢查某條邊(頂點對)是否存在,容易找出一個頂點的鄰接點,方便計算頂點的度。不過矩陣表示的不好在於,對於稀疏圖來說,鄰接矩陣的方式會浪費空間,因爲稀疏圖即邊很少,也就是二維數組裏很多元素都是0。矩陣在稀疏圖的情況下浪費空間也浪費時間,比如我想統計稀疏圖裏一共有多少條邊,把二維數組遍歷一遍,統計元素1有多少個,問題是1很少,所以造成很多對0的遍歷,是浪費時間的。

那麼如何解決鄰接矩陣浪費空間和時間的問題?我們知道線性表,線性表順序存儲需要實現分配好內存,這樣有可能造成空間浪費或空間不夠用,於是就有了鏈表的方法,同樣圖的表示方法也可以用鏈表的方式,就是鄰接表表示法。

鄰接表表示法,即用一個鏈表的集合,一個一維指針數組,依然用數組下標表示頂點。所以數組的大小就是圖中頂點的個數。

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

#define MaxVertex 100  /*最大頂點數*/
typedef int Vertex; /*頂點的下標表示頂點*/ 
typedef int WeightType; /*邊的權值類型爲整型*/
typedef char DataType; /*頂點存儲的數據的類型*/

/*邊的數據結構*/
typedef struct EdgeNode *PtrlEdgeNode;
struct EdgeNode {
	Vertex V1, V2;  /* 向邊<V1, V2>*/
	WeightType weight; /*權重*/
};
typedef PtrlEdgeNode Edge;

/*鄰接點的數據結構*/ 
typedef struct PointNode *PtrlPointNode;
struct PointNode {
	Vertex index; /*鄰接點的下標*/
	WeightType weight; /*邊的權重*/
	PtrlPointNode Next; /*指向下一個鄰接點的指針*/
};

/*頂點表頭結點的數據結構*/
typedef struct HeadNode {
	PtrlPointNode HeadEdge; /*邊的表頭結點指針*/
	DataType Data; /*頂點的數據*/
} PointList[MaxVertex]; /*鄰接表類型*/

/*圖結點的數據結構*/ 
typedef struct GraphNode *PtrlGraphNode;
struct GraphNode {
	int numv; /*頂點數*/
	int nume; /*邊數*/
	PointList PL; /*鄰接表*/
};
typedef PtrlGraphNode  ListGraphNode; /*鄰接表方式存儲圖*/

2629行,頂點的表頭就是一個結構體指針數組。指針類型是第1822行的鄰接點的數據結構。

/*創建一個有Vertexnum個頂點但沒有邊的圖*/
ListGraphNode CreatGraph(int Vertexnum) 
{
	Vertex i;
	ListGraphNode MyGraph;
	
	MyGraph=(ListGraphNode)malloc(sizeof(struct GraphNode));
	MyGraph->nume=0;
	MyGraph->numv=Vertexnum;
	
	for (i=0;i<MyGraph->numv;i++) {
		MyGraph->PL[i].HeadEdge=NULL; //初始化數組裏各個頭結點 
	}
	return MyGraph;
}
/*插入邊*/
void InsertEdge(ListGraphNode MyGraph, Edge E) 
{
	PtrlPointNode Node; /*鄰接點*/
	/*插入邊<V1, V2>*/
	/*爲 V2建立新的鄰接點*/ 
	Node=(PtrlPointNode)malloc(sizeof(struct PointNode));
	Node->index=E->V2; /*將V2插入V1的表頭*/
	Node->weight=E->weight;
	Node->Next=MyGraph->PL[E->V1].HeadEdge;
	MyGraph->PL[E->V1].HeadEdge=Node;
	
	/*如果是無向圖,還要插入邊<V2, V1>*/
	Node=(PtrlPointNode)malloc(sizeof(struct PointNode));
	Node->index=E->V1; /*將V2插入V1的表頭*/
	Node->weight=E->weight;
	Node->Next=MyGraph->PL[E->V2].HeadEdge;
	MyGraph->PL[E->V2].HeadEdge=Node;
}
/*創建圖*/
ListGraphNode BuildGraph()
{
	ListGraphNode MyGraph;
	Edge E;
	Vertex i,j,numv;
	
	printf("請輸入要讀入的頂點個數:");
	scanf("%d", &numv);
	MyGraph=CreatGraph(numv);
	
	printf("請輸入要讀入的邊數:");
	scanf("%d", &MyGraph->nume);
	if (MyGraph->nume!=0) {
		E=(Edge)malloc(sizeof(struct EdgeNode)); /*建立邊結點*/
		/*讀入邊:格式爲"起點 終點 權重"*/
		for (i=0;i<MyGraph->numv;i++) {
			scanf("%d %d %d", &E->V1, &E->V2, &E->weight);
			/*插入邊到圖裏*/
			InsertEdge(MyGraph, E);
		}
	}
	
	/*如果頂點有數據的話,讀入數據*/
	for (i=0;i<MyGraph->numv;i++) {
		scanf("%c", &MyGraph->PL[i].Data);
	}
	return MyGraph;
}

但是對於鄰接表表示法來說,一條邊必定是被存了兩次的,例如頂點對< 5, 9 >,在指針數組5的鏈表裏必定有9,在指針數組9的鏈表裏也必定有5。所以用鄰接表的話,表示的圖要是稀疏圖纔算省空間,越稀疏越好。而對於完全圖來說,用鄰接矩陣的方式更方便後面的操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章