1. 程式人生 > >鄰接表實現有向圖BFS、DFS、拓撲排序

鄰接表實現有向圖BFS、DFS、拓撲排序


圖的大家族

常用圖的儲存結構有兩種:鄰接矩陣,鄰接表。一個數組,一個連結串列,可見覆雜的資料結構是建立在基礎結構之上的,在這裡選擇鄰接表儲存,邊比較少時省空間。

圖按照有無方向,有無權重,分為四類

  • 無向無權:無向圖
  • 無向有權:無向網
  • 有向無權:有向圖
  • 有向有權:有向網

可見帶有權重稱為網,否則稱為圖。
圖可以看成邊權均為1,所以是特殊網。因此掌握了網,也就順帶會了圖

由於無向圖均有對稱性,所以大多問題較好處理。而有向圖無對稱性,情況多變,且生活中的應用也較多,所以這裡選擇有向圖處理

因此,本文用鄰接表儲存有向圖,並實現深度、廣度優先搜尋和拓撲排序

資料結構

依次定義以下資料結構
邊->表頭->鄰接表->圖

typedef struct VNode{
	int adjvex;
	struct VNode* nextarc;
}VNode;//邊 

typedef struct Adj{
	int data;
	VNode* firstarc;
}Adj,AdjList[MAX_VEXNUM];//表頭 

typedef struct{
	int n,m;
	AdjList vertices;//鄰接表 
}Graph;//圖
bool visited[MAX_VEXNUM];//訪問陣列,遍歷時的核心;全域性變數 

工具–佇列

(可參考佇列實現

//佇列基本操作:初始化->入隊->判空->獲取隊頭->出隊->遍歷測試 
typedef struct QNode{
	int data;
	struct QNode* next;
}QNode;
typedef struct Queue{
	QNode* front;
	QNode* rear;
}Queue;

實現步驟

首先得建立圖,然後實現查詢指定點的第一個鄰接點和下一個鄰接點,這是實現兩種遍歷的核心,最後利用佇列和入度表即可實現拓撲排序

建圖

採取檔案輸入,比較方便
檔案有向圖.txt內容(第一行為頂點,邊個數;餘下為三元組(u,v,w)u,v為兩個頂點,w為權值)

6 8
1 2 1 1 3 1 1 4 1
3 2 1 3 5 1
4 5 1
6 4 1 6 5 1

//檔案讀取資料建立有向圖/網  
void CreateGraph(Graph &G)
{
	fstream inFile("有向圖.txt",ios::in);
	if(!inFile)cout<<"Fail to open file!"<<endl;
	inFile>>G.n>>G.m;
	int n=G.n,m=G.m;cout<<n<<" "<<m<<endl;
	
	for(int i=0; i < n; i++){//初始化 
		G.vertices[i].data = i;
		G.vertices[i].firstarc = NULL;
	}
	int u,v,w;
	VNode* p;
//	Adj pfst;//連結串列頭 注意指標使用,傳值還是傳址 
	for(int i = 0; i < m; i++){
		inFile>>u>>v>>w;//cout<<u<<" "<<v<<" "<<w<<endl;
		u--;v--;//輸入從1開始,而儲存從0開始 
		p = (VNode*)malloc(sizeof(VNode));
		p->adjvex = v;
		p->nextarc = G.vertices[u].firstarc;
		G.vertices[u].firstarc = p;
	}
	inFile.close();
	//輸出除錯 
	for(int i = 0; i < n; i++){
//		pfst = G.vertices[i];
		p = G.vertices[i].firstarc;
		while(p != NULL){
			cout<<p->adjvex<<" ";
			p = p->nextarc;
		}cout<<endl;
	}
}

第一個鄰接點

查詢到第一個未訪問過的頂點立刻返回;若不存在,返回-1

//返回u的第一個鄰接點 
int FirstAdjvex(Graph G,int u)
{
	VNode* p = G.vertices[u].firstarc;
	while(p != NULL){
		if(visited[p->adjvex])p = p->nextarc;//跳過訪問過的 
		else return p->adjvex;
	}
	return -1;//表示未找到鄰接點 
}

下一個鄰接點

先找到鄰接點v的位置,從他的後一位再開始查詢第一個鄰接點

//返回相對於鄰接點u後的鄰接點 
int NextAdjvex(Graph G,int u,int v)
{
	VNode* p = G.vertices[u].firstarc;
	//先找到v 
	while(p != NULL){
		if(p->adjvex == v)break;
		else p = p->nextarc;
	}
	p = p->nextarc;
	while(p != NULL){//查詢下一個鄰接點 
		if(visited[p->adjvex])p = p->nextarc;
		else return p->adjvex;
	}
	return -1;//表示無下一鄰接點 
} 

DFS

深度優先搜尋,類似於樹的先序遍歷

//一次只能遍歷一個連通分支
void DFS(Graph G,int u)
{
	visited[u] = true;
	cout<<u+1<<" ";
	 
	for(int v = FirstAdjvex(G,u); v >= 0; v = NextAdjvex(G,u,v)){//尋求下一個鄰接點,為了回退時準備 
		if(!visited[v])DFS(G,v);
	} 
}
//總函式,可處理非連通圖
void DFSTraverse(Graph G)
{
	for(int i = 0; i < G.n; i++){
		visited[i] = false;
	}
	//考慮到非連通圖,才寫迴圈
	for(int v = 0; v < G.n; v++){//可計算連通分支數 
		if(!visited[v]){
			DFS(G,v);
			cout<<endl;
		}
	}
}

BFS

廣度優先:類似於樹的層次遍歷,佇列實現
二者區別在於,圖必須先訪問再入隊,否則會出現重複訪問一個點;而樹無所謂

//一次只能遍歷一個連通分支
void BFS(Graph G,int u)
{
	Queue Q;
	InitQueue(Q);
	//先訪問再入隊 
	visited[u] = true;
	cout<<u+1<<" ";
	EnQueue(Q,u);
	
	while(!IsEmpty(Q)){//隊非空 
		u = GetTop(Q)->data;
		DeQueue(Q);
		for(int v = FirstAdjvex(G,u); v >= 0; v = NextAdjvex(G,u,v)){//出隊元素u 的所有相鄰點入隊 
			if(!visited[v]){
				visited[v] = true;
				EnQueue(Q,v);
				cout<<v+1<<" ";
			}
		}
	}
}
//廣度優先遍歷 
void BFSTraverse(Graph G)
{
	//訪問陣列初始化 
	for(int i = 0; i < G.n; i++){
		visited[i] = false;
	}
	//不一定是連通圖 
	for(int i = 0; i < G.n; i++){
		if(!visited[i]){
			BFS(G,i);
			cout<<endl;
		}
	}
}

拓撲排序

拓撲排序,同樣用佇列實現,並且和圖一樣,先訪問,再入隊,不過需要維護一個入度表,每次出隊記得更新入度表

void TopologicalSort(Graph G)
{
	int n=G.n,m=G.m;//方便用 
	
	int indegree[n];//入度表 
	for(int i = 0; i < n; i++){//入度表初始化 
		indegree[i] = 0;
	}
	//=========計算每個點的入度========= 
	for(int i =0; i < n; i++){
		VNode* pcur = G.vertices[i].firstarc;
		while(pcur != NULL){
			indegree[pcur->adjvex]++;
			pcur = pcur->nextarc;
		}
	}
	
	Queue Q;
	InitQueue(Q);
	//初始化拓撲排序,尋找第一屆入度為0的點 
	for(int i = 0; i < n; i++){
		if(indegree[i] == 0){
			EnQueue(Q,i);
			indegree[i] = -1;//表示已訪問 
			cout<<i+1<<" ";
		}
	}
	while(!IsEmpty(Q)){
		int u = GetTop(Q)->data;
		DeQueue(Q);
		VNode* p = G.vertices[u].firstarc;
		while(p != NULL){//出隊的元素所有邊(僅有出邊)都要刪除,所以將與其相鄰的點的入度-1 
			if(indegree[p->adjvex] != -1)indegree[p->adjvex]--;
			p = p->nextarc;
		}
		//掃描剩餘點的入度,為0則標記且入隊 
		for(int i = 0; i < n; i++){
			if(indegree[i] != -1 && indegree[i] == 0){
				EnQueue(Q,i);
				indegree[i] = -1;
				cout<<i+1<<" ";
			}
		}	
	}
}

測試結果

在這裡插入圖片描述

小收穫

  • 學而時習之,果然不是蓋的,這學期伊始,編寫這些演算法還覺得有些難度,現在第二次在來編寫,嘩啦啦一下就成功啦,果然實踐出真知,哈哈哈哈h~
  • 最近剛好在複習資料結構,正好將過去學的知識整理總結下,加深系統理解,記錄下來以便日後複習用,就像寫日記般

完整Code

#include<iostream>
using namespace std;
#include<stdlib.h>
#include<fstream>
#define MAX_VEXNUM 30//頂點最大個數 

typedef struct VNode{
	int adjvex;
	struct VNode* nextarc;
}VNode;//邊 

typedef struct Adj{
	int data;
	VNode* firstarc;
}Adj,AdjList[MAX_VEXNUM];//表頭 

typedef struct{
	int n,m;
	AdjList vertices;//鄰接表 
}Graph;
bool visited[MAX_VEXNUM];//全域性變數 

//佇列基本操作:初始化->入隊->判空->獲取隊頭->出隊->遍歷測試 
typedef struct QNode{
	int data;
	struct QNode* next;
}QNode;
typedef struct Queue{
	QNode* front;
	QNode* rear;
}Queue;
//初始化 
void InitQueue(Queue &Q)
{
	Q.front = Q.rear = (QNode*)malloc(sizeof(QNode));
	Q.front->next = NULL;
}
//入隊 
void EnQueue(Queue &Q,int e)
{
	QNode* p = (QNode*)malloc(sizeof(QNode));
	p->data = e;
	p->next = NULL;
	Q.rear->next = p;
	Q.rear = p;
}
//判空 
bool IsEmpty(Queue Q)
{
	if(Q.front == Q.rear)return true;
	else return false;
}
//出隊 
void DeQueue(Queue &Q)
{
	if(IsEmpty(Q))return;
//	QNode* p = (QNode*)malloc(sizeof(QNode));
	QNode* p = Q.front->next;
	Q.front->next = p->next;
	if(p->next == NULL)Q.rear = Q.front;//佇列刪成空,隊尾回到隊頭 
	free(p);
	p = NULL; 
}
//獲取隊頂 
QNode* GetTop(Queue Q)
{
//	if(IsEmpty(Q))return NULL;
	return Q.front->next; 
}
//??? 遍歷 
void TraverseQueue(Queue Q)
{
	QNode* p = Q.front->next;
	while(p != NULL){
		cout<<p->data<<" ";
		p = p->next;
	}cout<<endl;
//	while(!IsEmpty(Q)){
//		cout<<GetTop(Q)->data<<" ";
//		DeQueue(Q);
//	}
} 
//檔案讀取資料建立有向圖/網  
void CreateGraph(Graph &G)
{
	fstream inFile("有向圖.txt",ios::in);
	if(!inFile)cout<<"Fail to open file!"<<endl;
	inFile>>G.n>>G.m;
	int n=G.n,m=G.m;cout<<n<<" "<<m<<endl;
	
	for(int i=0; i < n; i++){//初始化 
		G.vertices[i].data = i;
		G.vertices[i].firstarc = NULL;
	}
	int u,v,w;
	VNode* p;
//	Adj pfst;//連結串列頭 注意指標使用,傳值還是傳址 
	for(int i = 0; i < m; i++){
		inFile>>u>>v>>w;//cout<<u<<" "<<v<<" "<<w<<endl;
		u--;v--;//輸入從1開始,而儲存從0開始 
		p = (VNode*)malloc(sizeof(VNode));
		p->adjvex = v;
		p->nextarc = G.vertices[u].firstarc;
		G.vertices[u].firstarc = p;
	}
	inFile.close();
	//輸出除錯 
	for(int i = 0; i < n; i++){
//		pfst = G.vertices[i];
		p = G.vertices[i].firstarc;
		while(p != NULL){
			cout<<p->adjvex<<" ";
			p = p->nextarc;
		}cout<<endl;
	}
}
//返回u的第一個鄰接點 
int FirstAdjvex(Graph G,int u)
{
	VNode* p = G.vertices[u].firstarc;
	while(p != NULL){
		if(visited[p->adjvex])p = p->nextarc;//跳過訪問過的 
		else return p->adjvex;
	}
	return -1;//表示未找到鄰接點 
}
//返回相對於鄰接點u後的鄰接點 
int NextAdjvex(Graph G,int u,int v)
{
	VNode* p = G.vertices[u].firstarc;
	//先找到v 
	while(p != NULL){
		if(p->adjvex == v)break;
		else p = p->nextarc;
	}
	p = p->nextarc;
	while(p != NULL){//查詢下一個鄰接點 
		if(visited[p->adjvex])p = p->nextarc;
		else return p->adjvex;
	}
	return -1;//表示無下一鄰接點 
} 
//深度優先搜尋,類似於樹的先序遍歷 
void DFS(Graph G,int u)
{
	visited[u] = true;
	cout<<u+1<<" ";
	 
	for(int v = FirstAdjvex(G,u); v >= 0; v = NextAdjvex(G,u,v)){//尋求下一個鄰接點,為了回退時準備 
		if(!visited[v])DFS(G,v);
	} 
}
// 
void DFSTraverse(Graph G)
{
	for(int i = 0; i < G.n; i++){
		visited[i] = false;
	}
	//考慮到非連通圖,才寫迴圈
	for(int v = 0; v < G.n; v++){//可計算連通分支數 
		if(!visited[v]){
			DFS(G,v);
			cout<<endl;
		}
	}

            
           

相關推薦

鄰接實現BFSDFS排序

圖的大家族 常用圖的儲存結構有兩種:鄰接矩陣,鄰接表。一個數組,一個連結串列,可見覆雜的資料結構是建立在基礎結構之上的,在這裡選擇鄰接表儲存,邊比較少時省空間。 圖按照有無方向,有無權重,分為四類 無向無權:無向圖 無向有權:無向網 有向無權:有向圖

鄰接儲存實現dfsbfs

#include<iostream> #include<stdlib.h> #define MAX 20 using namespace std; class ArcNode { public: int adjvex; //儲存弧的終止位置 Ar

鄰接儲存實現DFS(遞迴+非遞迴)BFS(非遞迴)兩種遍歷

程式碼如下: #include<stdio.h> #include<stdlib.h> #include<string.h> #include<iostream> using namespace std; #define MA

的兩種儲存結構及四種形態——鄰接矩陣鄰接網。

宣告: 程式碼中有大量的註釋,所以此處就不再作出大量的解釋了。 一 :鄰接矩陣儲存結構 1.首先是各種型別與巨集的定義: 1 #include <iostream> 2 using namespace std; 3 //無窮大 4 #define INFINITY IN

鄰接實現的建立與輸出

1 #include<stdio.h> 2 #include <iostream> 3 #include<algorithm> 4 using namespace std; 5 #define MVNum 100 6 typedef struct ArcN

[] 7.28 找出u到v的所有路徑-鄰接)-DFS

題目來源:嚴蔚敏《資料結構》C語言版本習題冊 7.28 【題目】已知有向圖和圖中兩個頂點u和v,試編寫演算法求有向圖中從u到v的所有簡單路徑,並以下圖為例手工執行你的演算法,畫出相應的搜尋過程圖 【測試資料】 【結果】 【答案】 /*----------

BZOJ 2815 淺談必經點問題總結+序+倍增LCA滅絕樹求法

世界真的很大 昨天算是感覺到了真的有人這麼無聊 就是有這種人,也管不得了,還是收起心情才是 必經點問題在考試中也算是出現過好幾次了,之前都用了其他的蜜汁方法水過去,昨天終究還是用了什麼滅絕樹

演算法】DFS應用-排序

深度優先搜尋(DFS)演算法是最重要的圖遍歷演算法,基於DFS框架,可以匯出大量的圖演算法,圖的拓撲排序即為其中一個很典型的例子。 拓撲排序:給定一個有向圖,如何在保證“每個頂點都不會通過邊,指向其在此序列中的前驅頂點”這一前提下,將所有頂點排成一個線性序列。 例如: 在編寫教材時,

C語言利用鄰接矩陣的儲存方式實現和無的廣度優先搜尋(BFS

#include <stdio.h> #include <stdlib.h> #define Max_Vetex_Num 100 #define MAXSIZE 20 #define STACK_SIZE 30 typedef struct { int vexs[M

演算法與資料結構基礎8:C++實現——鄰接儲存

前面實現了連結串列和樹,現在看看圖。 連結串列是一對一的對應關係; 樹是一對多的對應關係; 圖是多對多的對應關係。 圖一般有兩種儲存方式,鄰接表和鄰接矩陣。 先看鄰接表。 鄰接表就是將圖中所有的點用一個數組儲存起來,並將此作為一個連結串列的頭, 連結串列中儲存跟這個點相鄰的

鄰接-建立無

#include<stdio.h>#include<stdlib.h>#define MAX_VERTEX_NUM 20#define OK  1#define ERROR 0 typedef int InfoType;       /* 該弧相關資訊

C語言利用鄰接矩陣的儲存方式實現和無的深度優先搜尋(DFS

C語言利用圖的鄰接矩陣的儲存方式實現有向圖和無向圖的深度優先搜尋(DFS) Description 圖採用鄰接矩陣儲存,圖中頂點數為n(0<n<20),頂點資訊為整數,依次為0,1,..,n-1。 編寫函式,輸入圖的型別,0:無向圖,1:有向圖;輸入圖的頂點數、邊數、邊的偶對

演算法與資料結構基礎9:C++實現——鄰接矩陣儲存

鄰接矩陣的儲存比鄰接表實現起來更加方便,也更加容易理解。 鄰接矩陣就是用一個二維陣列matrix來儲存每兩個點的關係。如果兩個點m,n之間有邊,將陣列matrix[]m[m]設為1,否則設為0。 如果有權,則將matrix[m]n[]設為權值,定義一個很大或者很小的數(只要

第六章 最短路徑——(Floyd-WarshallDijkstraBellman-Ford)

數組 opened 表示 printf 開始 style logs include 五行 一、Floyd-Warshall——加入點(多源最短路徑,核心算法只有五行) 城市之間的最短路徑 輸入: 4 8 1 2 2 1 3 6 1 4 4 2 3 3 3 1 7 3 4

鄰接建立無,深度優先搜尋遍歷輸出

1 #include<stdio.h> 2 #include<string.h> 3 #include <iostream> 4 #include<algorithm> 5 using namespace std; 6 #define MVNu

鄰接建立無,廣度優先搜尋遍歷輸出

1 #include<stdio.h> 2 #include<string.h> 3 #include <iostream> 4 #include<algorithm> 5 using namespace std; 6 #defin

(網)(網)的構造以及遍歷

構造圖採用的是鄰接表的方法,然後採用深度和廣度優先進行遍歷。(博主第一次寫構造方法的時候花了很久寫的很冗雜,雖然也實現了,但是感覺到處都在打補丁,拼拼湊湊寫出來的,後來用了一分鐘重寫了一個,秒通過!!!欲哭無淚啊~原因主要是因為不同的輸入格式導致的,以後些其他程式碼的時候要

鄰接矩陣實現的建立

#include<stdio.h> #define Maxsize 50 #define M 5000//定義無窮數值為5000 typedef struct { char vex[Maxsize];//頂點表 int arc[Maxsize][Maxsiz

使用鄰接儲存無

問題描述         使用鄰接表儲存下圖所示無向圖

HDU3342判圈DFS&&排序

ble 成了 target href tar -- targe space 排序 HDU3342 Legal or Not 題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=3342 題目意思:一群大牛互相問問題,大牛有不會的,會