1. 程式人生 > >圖的結構與操作

圖的結構與操作

圖的結構

1、鄰接矩陣結構

#define MAXSIZE 100
#define INFINITY 65535  //無窮大,表示兩點直接沒有路徑連線
typedef struct Mgraph{
	char vex[MAXSIZE];  //定義頂點陣列
	int arc[MAXSIZE][MAXSIZE];  //定義鄰接矩陣
	int numvex, numedge;    //定義頂點個數和邊的個數
};
無向矩陣的建立
void CreatMgraph(Mgraph *G){
	int i, j, k, weight; //weight表示邊上的權值
	scanf("%d,%d", &G->numvex, &G->numedge); //讀入頂點個數和邊個數
	for (i = 0; i < numvex; i++)
		scanf("%c", &G->vex[i]);   //讀入頂點資訊到陣列中
	for (i = 0; i < numvex; i++)
		for (j = 0; j < numvex; j++)
			G->arc[i][j] = INFINITY;  //初始化鄰接矩陣陣列為無窮大
	for (k = 0; k < numedge; k++){
		scanf("%d,%d,%d", &i, &j, &weight);  //讀入邊之間的路徑(vi,vj)和權值
		G->arc[i][j] = weight;
		G->arc[j][i] = weight; //此處為無向圖,因此矩陣的權值是對角對稱結構。如果為有向圖,則需要另考慮權值
	}
}

2、鄰接表的結構

typedef struct EdgeNode{
	int adjvex;  //鄰接點對應的下標
	int weight;  //權值
	struct EdgeNode *next;
};
typedef struct VexNode{  //頂點結點
	int data;    //頂點資訊
	struct EdgeNode *first;  //頭指標
};
typedef struct Graphlist{  //鄰接表結構
	VexNode adjlist[MAXSIZE];
	int numvex, numedge;  //頂點數和邊數
};
無向圖鄰接表的建立
void CreatGraphlist(Graphlist *G){
	int i, j, k,w;
	scanf("%d,%d", &G->numvex, &G->numedge); //讀入邊和頂點的個數
	for (i = 0; i < numvex; i++){
		scanf("%c", &G->adjlist[i].data);  //讀入頂點資訊
		G->adjlist[i].first = nullptr;  //初始化頭指標
	}
	for (k = 0; k < numedge; k++){
		scanf("%d,%d,%d", &i, &j, &w); //讀入邊(vi,vj)以及對應的權值
		EdgeNode *e = (EdgeNode *)malloc(sizeof(EdgeNode));
		e->adjvex = j;
		e->weight = w;
		e->next = G->adjlist[i].first;
		G->adjlist[i].first = e;   //此處用的是連結串列操作的頭插法
	}			
}
還有十字連結串列、鄰接多重連結串列、邊集陣列結構

關於圖的遍歷,主要為兩種,深度優先遍歷DFS和廣度優先遍歷BFS

廣度優先遍歷DFS--類似於樹的前序遍歷操作,

鄰接矩陣的深度優先遍歷

bool visited[MAX]; //使用一個標誌陣列來儲存訪問的資訊
void DFS(MGraph G, int i){   //從第i個頂點開始遍歷的DFS遞迴演算法
	int j;
	visited[i] = true;     //true表示該頂點被訪問過
	printf("%c", G.vex[i]);
	for (j = 0; j <G.numvex; j++)
		if (G.arc[i][j] != INFINITY &&!visited[j])
			DFS(G, j);	
}
void DFSTraverse(MGraph G){  //DFS遍歷操作
	int i;
	for (i = 0; i <G.numvex; i++)
		visited[i] = false; //初始化標誌陣列,表示所有頂點都未訪問過
	for (i = 0; i < G.numvex; i++)
		if (!visited[i])   // 連通圖的話,只執行一次,非連通圖會至少兩次
			DFS(G, i);
}

鄰接表的深度優先遍歷

void DFS(Graphlist *G, int i){  //從第i個頂點開始遍歷的DFS遞迴演算法
	EdgeNode *p;  //相當於鄰接矩陣中的j
	visited[i] = true;
	printf("%c", G->adjlist[i].data);
	p = G->adjlist[i].first; //相當於j=0
	while (p){
		if (!visited[i])
			DFS(G, p->adjvex);
		p = p->next;    //相當於j++
	}
}
void DFSTraverse(Graphlist *G){   //DFS遍歷操作
	int i;
	for (i = 0; i < G->numvex; i++)
		visited[i] = false;
	for (i = 0; i < G->numvex; i++)
		if (!visited[i]) // 連通圖的話,只執行一次,非連通圖會至少兩次
			DFS(G, i);
}

廣度優先遍歷BFS --類似於樹的層遍歷方式,需要藉助一個佇列輔助來實現

佇列Queue的操作在前面總結當中提過,不再重新定義,不過要注意,根據實際情況,隊列當中的資料型別可能會是圖 sturct MGraph型別或者struct Graphlist型別

鄰接矩陣的廣度遍歷BFS

void BFSTraverse(MGraph G){
	int i, j;
	Queue Q;  //輔助佇列
	for (i = 0; i < G.numvex; i++)
		visited[i] = false;
	InitQueue(&Q); //初始化佇列
	for (i = 0; i < G.numvex; i++){
		if (!visited[i]){
			visited[i] = true;    // 連通圖的話,只執行一次,非連通圖會至少兩次
			printf("%c", G.vex[i]);
			EnQueue(&Q, &i);    //從第i個頂點開始BFS遍歷
			while (!QueueEmpty(Q)){  //佇列不為空的時候繼續遍歷
				Dequeue(&Q, &i);
					for (j = 0; j < G.numvex; j++){
					if (G.arc[i][j] != INFINITY && !visited){  //判斷該頂點與其他頂點是否相連,並列印相連的頂點
						visited[j] = true;
						printf("%c", G.vex[j]);
						Enqueue(&Q, &i);          //將相鄰的頂點存入佇列中
					}
				}
			}
		}
	 }
}

鄰接表的BFS
void BFSTraverse(Graphlist *G){
	EdgeNode *p;  //相當於鄰接表中的j
	int i;
	Queue Q;
	for (i = 0; i < G->numvex; i++)
		visited[i] = false;
	InitQueue(&Q);
	for (i = 0; i < G->numvex; i++){
		if (!visited[i]){
			visited[i] = true;  // 連通圖的話,只執行一次,非連通圖會至少兩次
			printf("%c", G->adjlist[i].data);
			EnQueue(&Q, &i);
			while (!QueueEmpty(Q)){  //佇列不為空的時候繼續遍歷
				DeQueue(&Q, &i);
				p = G->adjlist[i].first; //相當於j=0
				while (p){
					if (!visited[p->adjvex]){
						visited[p->adjvex] = true;
						printf("%c", G->adjlist[p->adjvex].data);
						Enqueue(&Q, &i);
					}
					p = p->next;  //相當於j++
				}
			}

		}
	}
}