1. 程式人生 > >鄰接表實現--圖的深度優先遍歷DFS和廣度優先遍歷BFS

鄰接表實現--圖的深度優先遍歷DFS和廣度優先遍歷BFS

          圖論中一個基本的概念就是遍歷。就是訪問到圖的每一個頂點,同時每個頂點只訪問一次。

          DFS和BFS的概念和思路網上說明的很詳細了。但是網上很多程式碼實現有缺陷,基本都沒有考慮圖不連通的情況,比如某個頂點A和其它任何一個頂點都不關聯,那麼這個頂點A就訪問不到了。如果遍歷的起點剛好是孤立的頂點A,就只能訪問頂點A了,其它頂點就訪問不到了。

          我這邊的程式碼就是增加了這些情況的處理,確保每個頂點都能可以訪問到。

          完整的程式碼如下(通過鄰接表實現圖):

#define MAX_VERTEX 16

typedef enum
{
	UNDIRECTED_GRAPH = 0, //無向圖
	DIRECTED_GRAPH = 1, //有向圖
	UNDIRECTED_NET = 2, //無向網
	DIRECTED_NET = 3, //有向網
}GRAPH_TYPE;

typedef struct _ARC_NODE
{
	int index; //鄰接頂點在頂點陣列中的索引值
	struct _ARC_NODE* next;//指向下一個相鄰頂點
}ARC_NODE,*PARC_NODE;

typedef struct _VERTEX
{
	char data;//頂點資料值
	PARC_NODE outHead;//出邊表頭指標
	PARC_NODE inHead; //入邊表頭指標
}VERTEX,*PVERTEX;

typedef struct
{
	GRAPH_TYPE type; //圖的型別
	int vertexCount;//頂點個數
	BOOL visitFlag[MAX_VERTEX];
	VERTEX vertex[MAX_VERTEX]; 
}LINKED_GRAPH,*PLINKED_GRAPH; //鄰接表方式的圖或者網

void DFS(PLINKED_GRAPH graph,int startVertexIndex);
void BFS(PLINKED_GRAPH graph,int startVertexIndex);


void Visit(PLINKED_GRAPH graph,int vIndex)
{
	graph->visitFlag[vIndex] = TRUE; //設定已訪問標誌
	printf("Visit Vertex: %c\r\n",graph->vertex[vIndex].data);
}

void InitVistFlag(PLINKED_GRAPH graph)
{
	for(int i=0;i<graph->vertexCount;i++)
	{
		graph->visitFlag[i] = FALSE;
	}
}

void DFS(PLINKED_GRAPH graph,int startVertexIndex)
{
	stack<int> s; //訪問棧
	s.push(startVertexIndex);
	while(!s.empty())
	{
		int vertexIndex = s.top();
		if(!graph->visitFlag[vertexIndex])//未訪問過
		{
			Visit(graph,vertexIndex);
		}

		s.pop();//vertexIndex頂點出棧

		//這裡我們通過出度表來遍歷
		PARC_NODE p = graph->vertex[vertexIndex].outHead;
		while(p)
		{
			//與vertexIndex相鄰的頂點並且未訪問過的頂點全部入棧
			if(!graph->visitFlag[p->index])
			{
				s.push(p->index);
			}
			p = p->next;//指向下一個與vertexIndex相鄰的頂點
		}
	}

	//圖並不一定是連通的,因此要確保每個頂點都遍歷過
	for(int i=0;i<graph->vertexCount;i++)
	{
		if(!graph->visitFlag[i])
		{
			printf("Not Connected vertex start DFS: %c\r\n",graph->vertex[i]);
			DFS(graph,i);
		}
	}
}

void BFS(PLINKED_GRAPH graph,int startVertexIndex)
{
	queue<int> q; //訪問佇列
	q.push(startVertexIndex);//起始訪問的頂點入隊

	while(!q.empty())
	{
		int vertexIndex = q.front();
		if(!graph->visitFlag[vertexIndex])//未訪問過
		{
			Visit(graph,vertexIndex);
		}

		q.pop();//vertexIndex頂點出隊

		//這裡我們通過出度表來遍歷
		PARC_NODE p = graph->vertex[vertexIndex].outHead;
		while(p)
		{
			//與vertexIndex相鄰的頂點並且未訪問過的頂點全部入隊
			if(!graph->visitFlag[p->index])
			{
				q.push(p->index);
			}
			p = p->next;//指向下一個與vertexIndex相鄰的頂點
		}
	}

	//圖並不一定是連通的,因此要確保每個頂點都遍歷過
	for(int i=0;i<graph->vertexCount;i++)
	{
		if(!graph->visitFlag[i])
		{
			printf("Not Connected vertex start BFS: %c\r\n",graph->vertex[i]);
			BFS(graph,i);
		}
	}
}

void InitLinkedGraph(PLINKED_GRAPH graph)
{
	graph->type = UNDIRECTED_GRAPH; //無向圖
	graph->vertexCount = 10;
	for(int i=0;i<graph->vertexCount;i++)
	{
		graph->vertex[i].data = 'A'+i; //頂點為'A','B','C'等等
	}

	graph->vertex[0].outHead = new ARC_NODE;
	graph->vertex[0].outHead->index = 1; //AB有邊
	graph->vertex[0].outHead->next = new ARC_NODE;
	graph->vertex[0].outHead->next->index = 4;//AE有邊
	graph->vertex[0].outHead->next->next = NULL;

	graph->vertex[1].outHead = new ARC_NODE;
	graph->vertex[1].outHead->index = 0; //BA有邊
	graph->vertex[1].outHead->next = new ARC_NODE;
	graph->vertex[1].outHead->next->index = 3;//BD有邊
	graph->vertex[1].outHead->next->next = NULL;

	graph->vertex[2].outHead = new ARC_NODE;
	graph->vertex[2].outHead->index = 4; //CE有邊
	graph->vertex[2].outHead->next = new ARC_NODE;
	graph->vertex[2].outHead->next->index = 5;//CF有邊
	graph->vertex[2].outHead->next->next = new ARC_NODE;
	graph->vertex[2].outHead->next->next->index = 6;//CG有邊
	graph->vertex[2].outHead->next->next->next = new ARC_NODE;
	graph->vertex[2].outHead->next->next->next->index = 7; //CG有邊
	graph->vertex[2].outHead->next->next->next->next = NULL;

	graph->vertex[3].outHead = new ARC_NODE;
	graph->vertex[3].outHead->index = 1; //DB有邊
	graph->vertex[3].outHead->next = NULL;

	graph->vertex[4].outHead = new ARC_NODE;
	graph->vertex[4].outHead->index = 2; //EC有邊
	graph->vertex[4].outHead->next = NULL;

	graph->vertex[5].outHead = new ARC_NODE;
	graph->vertex[5].outHead->index = 2; //FC有邊
	graph->vertex[5].outHead->next = NULL;

	graph->vertex[6].outHead = new ARC_NODE;
	graph->vertex[6].outHead->index = 2; //GC有邊
	graph->vertex[6].outHead->next = new ARC_NODE;
	graph->vertex[6].outHead->next->index = 8;//GI有邊
	graph->vertex[6].outHead->next->next = new ARC_NODE;
	graph->vertex[6].outHead->next->next->index= 9;//GJ有邊
	graph->vertex[6].outHead->next->next->next = NULL;

	graph->vertex[7].outHead = new ARC_NODE;
	graph->vertex[7].outHead->index = 2; //HC有邊
	graph->vertex[7].outHead->next = NULL;

	graph->vertex[8].outHead = new ARC_NODE;
	graph->vertex[8].outHead->index = 6; //IG有邊
	graph->vertex[8].outHead->next = NULL;

	graph->vertex[9].outHead = new ARC_NODE;
	graph->vertex[9].outHead->index = 6; //JG有邊
	graph->vertex[9].outHead->next = NULL;
}

void PrintLinkedGraph(PLINKED_GRAPH graph)
{
	printf("Linked Graph Info:\r\n");
	for(int i=0;i<graph->vertexCount;i++)
	{
		printf("%2c",graph->vertex[i].data);
		PARC_NODE p = graph->vertex[i].outHead;
		while(p)
		{
			printf(" --> %2c",graph->vertex[p->index].data);
			p = p->next;
		}
		printf("\r\n");
	}
	printf("\r\n");
}

void DestroyLinkedGraph(PLINKED_GRAPH graph)
{
	for(int i=0;i<graph->vertexCount;i++)
	{
		PARC_NODE p = graph->vertex[i].outHead;
		while(p)
		{
			PARC_NODE pNext = p->next;

			p->next = NULL;
			delete p;

			p = pNext;
		}
	}
}

void TestLinkedGraph()
{
	LINKED_GRAPH graph = {UNDIRECTED_GRAPH,0};
	InitLinkedGraph(&graph);
	InitVistFlag(&graph);
	PrintLinkedGraph(&graph);

	printf("Linked Graph DFS:\r\n");
	DFS(&graph,0);
	printf("\r\n");

	InitVistFlag(&graph);
	printf("Linked Graph DFS:\r\n");
	BFS(&graph,0);
	printf("\r\n");

	DestroyLinkedGraph(&graph);
}