圖的鄰接矩陣表示


圖的鄰接矩陣表示

圖基概念(Graph)

  • 包含

    • 一組頂點:通常用V (Vertex) 表示頂點集合
    • 一組邊:通常用E (Edge) 表示邊的集合
    • 邊是頂點對:(v, w) ∈E ,其中v, w ∈ V有向邊<v, w> 表示從v指向w的邊(單行線)不考慮重邊和自回路
  • 無向圖:邊是無向邊(v, w)

  • 有向圖:邊是有向邊<v, w>

  • 連通:如果從V到W存在一條(無向)路徑,則稱V和W是連通的

  • 連通圖(Connected Graph):如果對於圖的任一兩個頂點v、w∈V,v和w都是連通的,則稱該圖為連通圖。圖中任意兩頂點均連通。

  • 連通分量(Connected Component):無向圖中的極大連通子圖。

    • 極大頂點數:再加1個頂點就不連通了
    • 極大邊數:包含子圖中所有頂點相連的所有邊
  • 強連通:有向圖中頂點V和W之間存在雙向路徑,則稱V和W是強連通的。

  • 強連通圖:有向圖中任意兩頂點均強連通。

  • 強連通分量:有向圖的極大強連通子圖。

  • 路徑:V到W的路徑是一系列頂點{V, v1, v2, …,vn, W}的集合,其中任一對相鄰的頂點間都有圖中的邊。路徑的長度是路徑中的邊數(如果帶權,則是所有邊的權重和)。

  • 如果V到W之間的所有頂點都不同,則稱簡單路徑

  • 回路:起點等於終點的路徑

鄰接矩陣

  • 圖的鄰接矩陣存儲方式就是用一個二維數組來表示。鄰接矩陣G[N][N]——N個頂點從0到N-1編號頂點i、j有邊,則G[i][j] = 1 或邊的權重

  • 鄰接矩陣的優點

    • 直觀、簡單、好理解
    • 方便檢查任意一對頂點間是否存在邊
    • 方便找任一頂點的所有“鄰接點”(有邊直接相連的頂點)
    • 方便計算任一頂點的“度”(從該點發出的邊數為“出度”,指向該點的邊數為“入度”)
    • 無向圖:對應行(或列)非0元素的個數
    • 有向圖:對應行非0元素的個數是“出度”;對應列非0元素的個數是“入度”
  • 鄰接矩陣的缺點

    • 浪費空間—— 存稀疏圖(點很多而邊很少)有大量無效元素
    • 對稠密圖(特別是完全圖)還是很合算的
    • 浪費時間—— 統計稀疏圖中一共有多少條邊

BFS廣度優先搜索(Breadth First Search, BFS)

  • 運用隊列,將頂點V的每個鄰接點進隊。(類似於樹的層先遍歷)

  • 若有N個頂點、E條邊,時間復雜度是
    - 用鄰接表存儲圖,有O(N+E)
    - 用鄰接矩陣存儲圖,有O(N^2)

DFS深度優先搜索索(Depth First Search, DFS)

  • 用遞歸(類似於樹的先序遍歷)。

  • ListComponents 圖不連通時,列出各連通分量。

  • 若有N個頂點、E條邊,時間復雜度是
    - 用鄰接表存儲圖,有O(N+E)
    - 用鄰接矩陣存儲圖,有O(N^2)

  • 深度優先遍歷算法的非遞歸實現需要了解深度優先遍歷的執行過程,設計一個棧來模擬遞歸實現中系統設置的工作棧,算法的偽代碼描述為:

  • 假設圖采用鄰接矩陣作為存儲結構,具體算法如下:

測試代碼:


/*!
 * \file 圖的鄰接矩陣表示.cpp
 *
 * \author ranjiewen
 * \date 2017/04/10 23:20
 *
 * 
 */

#include <iostream> 
#include <cstdio>
#include <cstdlib>
#include <queue>

using namespace std;

/* 圖的鄰接矩陣表示法 */
#define  MaxVertexNum 100 /*最大頂點數設為100*/
#define  INFINITY 65535 /*設為雙字節無符號整數的最大值為65535*/
typedef int Vertex; /*用頂點下標表示頂點,為整型*/
typedef int WeightType; /*邊的權值設為整型*/
typedef char DataType; /*頂點存儲的數據類型設為字符型*/

/*邊的定義*/
typedef struct ENode* PtrToENode;
struct ENode
{
	Vertex V1, V2; //有向邊<v1,v2>
	WeightType Weight;//權重
};
typedef PtrToENode Edge;

/*圖結點的定義*/
typedef struct GNode *PtrToGNode;
struct GNode
{
	int Nv; //頂點樹
	int Ne; //邊數
	WeightType G[MaxVertexNum][MaxVertexNum]; //鄰接矩陣
	DataType Data[MaxVertexNum];// 存頂點的數據
	//注意:很多情況下,頂點無數據,此時Data[]可以不用出現
};
typedef PtrToGNode MGraph; /*用鄰接矩陣存儲的圖類型*/

bool Visited[MaxVertexNum] = { false };

MGraph CreateGraph(int VertexNum)
{
	/*初始化一個有VertexNum個頂點但沒有邊的圖*/
	Vertex V, W; /*頂點的下標*/
	MGraph Graph;

	Graph = (MGraph)malloc(sizeof(struct GNode)); /*建立圖*/
	Graph->Nv = VertexNum;
	Graph->Ne = 0;
	//初始化鄰接矩陣
	//注意:這里默認頂點編號從0開始到(Graph->Nv - 1)
	for (V = 0; V < Graph->Nv;V++)
	{
		for (W = 0; W < Graph->Nv;W++)
		{
			Graph->G[V][W] = INFINITY;
		}
	}
	return Graph;
}

void InsertEdge(MGraph Graph,Edge E)
{
	//插入邊<v1,v2>
	Graph->G[E->V1][E->V2] = E->Weight;
	//若是無向圖,還要插入邊<v2,v1>
	Graph->G[E->V2][E->V1] = E->Weight;
}

MGraph BuildGraph()
{
	MGraph Graph;
	Edge E;
	Vertex V;
	int Nv, i;

	scanf("%d", &Nv); /*讀入頂點個數*/
	Graph = CreateGraph(Nv); /* 初始化有Nv個頂點但沒有邊的圖 */
	
	scanf("%d", &(Graph->Ne)); /*讀入邊數*/
	if (Graph->Ne!=0) //如果有邊
 	{
		E = (Edge)malloc(sizeof(struct ENode)); //建立邊結點
		//讀入邊,格式為:起點,中點,權重;插入鄰接矩陣
		for (i = 0; i < Graph->Ne;i++)
		{
			scanf("%d %d %d", &E->V1, &E->V2, &E->Weight);
			//注意:如果權重不是整型,weight的讀入格式要改變
			InsertEdge(Graph, E);
		}
	}

	//如果頂點有數據的話,讀入數據
	for (V = 0; V < Graph->Nv;V++)
	{
		//scanf("%c", &(Graph->Data[V]));
	}
	return Graph;
}

/* 鄰接矩陣存儲的圖 - BFS */

/* IsEdge(Graph, V, W)檢查<V, W>是否圖Graph中的一條邊,即W是否V的鄰接點。  */
/* 此函數根據圖的不同類型要做不同的實現,關鍵取決於對不存在的邊的表示方法。*/
/* 例如對有權圖, 如果不存在的邊被初始化為INFINITY, 則函數實現如下:         */
bool IsEdge(MGraph Graph, Vertex V, Vertex W)
{
	return Graph->G[V][W] < INFINITY ? true : false;
}

void InitVisited()
{
	for (int i = 0; i < MaxVertexNum;i++)
	{
		Visited[i] = false;
	}
}

void Visit(Vertex v)
{
	printf("%d ", v);
}

//連通下的DFS和BFS
void BFS(MGraph Graph, Vertex S, void(*Visit)(Vertex))
{  
	/* 以S為出發點對鄰接矩陣存儲的圖Graph進行BFS搜索 */
	queue<Vertex> Q;
	Vertex V, W;

	Visit(S);
	Visited[S] = true; 
	Q.push(S);

	while (!Q.empty()) {
		V = Q.front();
		Q.pop();
		for (W = 0; W < Graph->Nv; W++)  /* 對圖中的每個頂點W */
		/* 若W是V的鄰接點並且未訪問過 */
		if (!Visited[W] && IsEdge(Graph, V, W)) 
		{
			/* 訪問頂點W */
			Visit(W);
			Visited[W] = true;
			Q.push(W);
		}
	}
}

void DFS(MGraph Graph, Vertex S, void(*Visit)(Vertex))
{
	/* 以V為出發點對鄰接表存儲的圖Graph進行DFS搜索 */
	Visited[S] = true;
	Visit(S);
	for (Vertex w = 0; w < Graph->Nv; w++) {
		if (IsEdge(Graph, S, w) && Visited[w] == false) {
			DFS(Graph, w, Visit);
		}
	}
}

//非連通下的遍歷
Vertex listDFS(MGraph Graph, void(*Visit)(Vertex))
{
	Vertex i;
	for (i = 0; i < Graph->Nv; i++) {
		if (Visited[i] == false)
			break;
	}
	if (i == Graph->Nv)
		return 0;
	DFS(Graph, i, Visit);
	printf("\n");

	return listDFS(Graph, Visit);
}

void DFSListComponents(MGraph Graph, void(*Visit)(Vertex))
{
	for (Vertex i = 0; i < Graph->Nv; i++) {
		if (Visited[i] == false) {
			DFS(Graph, i, Visit);
			printf("\n");
		}
	}
}
void BFSListComponents(MGraph Graph, void(*Visit)(Vertex))
{
	for (Vertex i = 0; i < Graph->Nv; i++) {
		if (Visited[i] == false) {
			BFS(Graph, i, Visit);
			printf("\n");
		}
	}
}

int main()
{
	MGraph graph;
	graph = BuildGraph();
	InitVisited();
	listDFS(graph, &Visit);
	InitVisited();
	DFSListComponents(graph, &Visit);
	InitVisited();
	//BFS(graph,0,&Visit);
	BFSListComponents(graph, &Visit);
	return 0;
}


結果:

Reference

數據結構學習筆記05圖 (鄰接矩陣 鄰接表-->BFS DFS、最短路徑)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM