对于一个图或者网络
G
=
(
V
,
E
)
\mathcal{G}=(V,E)
G=(V,E)来说,其数据一般有两种表示方式,第一种是一个行数和列数均为
∣
V
∣
|V|
∣V∣的邻接矩阵,第二种是只记录连边信息的邻接表.如果网络的规模很大,那么邻接矩阵的数据空间需要
O
(
∣
V
∣
2
)
O(|V|^2)
O(∣V∣2)复杂度,所以是不现实的,这时一般采用邻接表的方式进行表示.在C/C++的数据结构中,先定义一个结构体数组表示所有的节点,然后定义每个节点的多条连边,连边信息以链表形式关联起来,从而形成邻接表.
在网络上计算单源最短路径一般使用Dijkstra算法,时间复杂度为
O
(
∣
V
∣
2
)
O(|V|^2)
O(∣V∣2),还有一种经过堆优化的Dijkstra算法其复杂度可以降低一些.对于特殊情况的无权网络,Dijkstra算法可以等同于广度优先搜索BFS,其时间复杂度为
O
(
∣
V
∣
+
∣
E
∣
)
O(|V|+|E|)
O(∣V∣+∣E∣).默认图的数据以txt格式保存,每行代表一个连边,其实节点和终止节点以空格符隔开,最后以C++实现图的数据读取和广度有限搜索,并将其封装为一个头文件Graph.h.
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<string>
#include<string.h>
#include<queue>
#define MAX_LINE 16
using namespace std;
struct Edges{
int vertexIndex;
int distance;
struct Edges *nextEdge;
};
struct Vertices{
int name;
Edges *firstEdge = NULL;
Edges *tailEdge;
};
struct edgeList{
int vertexFrom;
int vertexTo;
struct edgeList *next;
};
void readTxtFile(struct edgeList *head,char const *fileName,int &numVertices,int &numEdges);
class Graph{
public:
int numVertices;
int numEdges;
Vertices *vertices;
Graph(){/*无参构造函数*/
this -> numVertices = -1;
this -> numEdges = -1;
this -> vertices = NULL;
};
void readGraph(char const *fileName);/*读取图网络数据文件*/
void BFS(int vertexIndex,int *distList);/*图结构的广度优先搜索算法*/
~Graph();
};
Graph::~Graph(){/*析构函数*/
int deleteNumEdges = 0;
for(int i = 0;i < this -> numVertices;i++){
while(this -> vertices[i].firstEdge != NULL){
this -> vertices[i].tailEdge = this -> vertices[i].firstEdge;
this -> vertices[i].firstEdge = this -> vertices[i].tailEdge -> nextEdge;
this -> vertices[i].tailEdge = NULL;
delete(this -> vertices[i].tailEdge);
deleteNumEdges++;
}
this -> vertices[i].tailEdge = NULL;
}
delete[] this -> vertices;
this -> vertices = NULL;
cout<<"删除的连边数为:"<<deleteNumEdges<<endl;
}
void Graph::BFS(int vertexIndex,int *distList){
bool *visitedList = new bool[this -> numVertices];
struct Edges *p;/*创建指针*/
for(int i = 0;i < this -> numVertices;i++){
visitedList[i] = false;
}
visitedList[vertexIndex] = true;
distList[vertexIndex] = 0;
queue<int> *q = new queue<int>;/*用STL队列实现,#include<queue>*/
q -> push(vertexIndex);/*入队*/
while(!q -> empty()){/*队列非空时执行循环*/
int u = q -> front();/*取队头元素*/
p = this -> vertices[u].firstEdge;
while(p != NULL){
if(!visitedList[p -> vertexIndex]){
q -> push(p -> vertexIndex);/*入队*/
distList[p -> vertexIndex] = distList[u] + 1;/*路径长度+1*/
visitedList[p -> vertexIndex] = true;/*置为已考察*/
}
p = p -> nextEdge;
}
q -> pop();/*出队*/
}
delete[] visitedList;
}
void Graph::readGraph(char const *fileName){
/*--------------------读取网络数据--------------------*/
struct edgeList *head = new edgeList;/*创建头节点*/
readTxtFile(head,fileName,this -> numVertices,this -> numEdges);
/*--------------------构建邻接表--------------------*/
struct edgeList *p = head -> next;
struct Edges *edges;
this -> vertices = new Vertices[this -> numVertices];
for(int i = 0;i < this -> numEdges;i++){/*遍历链表并打印数据*/
edges = new Edges;
edges -> vertexIndex = p -> vertexTo;
edges -> nextEdge = NULL;
if(this -> vertices[p -> vertexFrom].firstEdge == NULL){
this -> vertices[p -> vertexFrom].firstEdge = edges;
this -> vertices[p -> vertexFrom].tailEdge = edges;
}
else{
this -> vertices[p -> vertexFrom].tailEdge -> nextEdge = edges;
this -> vertices[p -> vertexFrom].tailEdge = edges;
}
p = p -> next;
}
/*内存释放*/
while(head != NULL){/*删除链表*/
p = head;
head = p -> next;
delete(p);
}
head = NULL;
p = NULL;
//edges = NULL;
}
void readTxtFile(struct edgeList *head,char const *fileName,int &numVertices,int &numEdges){
char buf[MAX_LINE];/*缓冲区*/
FILE *fid;/*文件指针*/
int len;/*行字符个数*/
int minVertexNum = 2;/*最小的节点标号*/
int maxVertexNum = 0;/*最大的节点标号*/
int vertexFrom;/*起始节点*/
int vertexTo;/*终止节点*/
char *buff;/*复制字符串用于分割*/
char *strOfInt;/*复制分割后的字符串*/
struct edgeList *p1 = NULL;/*开辟并保存新节点*/
bool isDirected = false;
struct edgeList *p2 = head;/*保存链表最后一个节点*/
head -> vertexFrom = 0;
head -> vertexTo = 1492;
if((fid = fopen(fileName,"r")) == NULL){
perror("fail to read");
exit(1);
}
numEdges = 0;/*连边总数赋初值*/
while(fgets(buf,MAX_LINE,fid) != NULL){
buff = buf;
strOfInt = strsep(&buff, " ");
vertexFrom = atoi(strOfInt);
strOfInt = strsep(&buff, " ");
vertexTo = atoi(strOfInt);
if(minVertexNum > vertexFrom){
minVertexNum = vertexFrom;
}
if(minVertexNum > vertexTo){
minVertexNum = vertexTo;
}
if(maxVertexNum < vertexFrom){
maxVertexNum = vertexFrom;
}
if(maxVertexNum < vertexTo){
maxVertexNum = vertexTo;
}
strOfInt = strsep(&buff, " ");
/*将数据写到链表当中存储*/
p1 = new edgeList;
p1 -> vertexFrom = vertexFrom;
p1 -> vertexTo = vertexTo;
p2 -> next = p1;
p2 = p1;
if(!isDirected){
p1 = new edgeList;
p1 -> vertexFrom = vertexTo;
p1 -> vertexTo = vertexFrom;
p2 -> next = p1;
p2 = p1;
numEdges++;
}
numEdges++;/*文件数据的总行数,网络边总数*/
}
if(minVertexNum != 0){/*保证节点编号从零开始*/
p2 = head;
while(p2 != NULL){
p2 -> vertexFrom = p2 -> vertexFrom - minVertexNum;
p2 -> vertexTo = p2 -> vertexTo - minVertexNum;
p2 = p2 -> next;
}
}
free(buff);/*释放指针内存*/
buff = NULL;/*将指针置为空*/
p2 -> next = NULL;
int isClosed = fclose(fid);
numVertices = maxVertexNum - minVertexNum + 1;/*网络节点的总数*/
}