1、图的定义
图G由两个集合顶点集V和边集E组成,记为G = (V , E)
2、邻接矩阵
(1)表示顶点之间相邻关系的矩阵叫邻接矩阵。具有n个顶点的图G=(V,E)是具有下列性质的n阶方阵:
A[i][j]=1,若(vi,vj)或<vi,vj>是E(G)中的边;
A[i][j]=0,若(vi,vj)或<vi,vj>不是E(G)中的边;
(2)若G是网络,则邻接矩阵可定义为:
A[i][j]=wij,若(vi,vj)或<vi,vj>是E(G)中的边;
A[i][j]= ∞,若(vi,vj)或<vi,vj>不是E(G)中的边;
邻接矩阵对有向图和无向图都适用,对带权图和非权图同样适用。
无向图的邻接矩阵显然为对称矩阵。
3、顶点的度
在无向图中顶点V的度定义为以该顶点为一个端点的边的数目,称为顶点的度。
若G是有向图,则v的出度是以v为始点的边的个数,v的入度是以v为终点的边的个数。
借助邻接矩阵,可以很容易求出图中顶点的度。
(1)无向图-邻接矩阵的第i行(或第i列)的非零元素的个数是顶点Vi的度。
(2)有向图-邻接矩阵第i行的非零元素的个数为顶点Vi的出度;第i列的非零元素的个数为顶点Vi的入度。
二、代码实现
下面要给出的代码,是以无向权图为例子。对于无向非权图,有向图权图,有向非权图,对插入删除顶点和边的代码做适量修改即可。
(1) graph.h头文件
//graph.h
#include <climits>
#include <vector>
#include <fstream>
using namespace std;
template<typename T>
class Graph
{
private:
//int max_size ; //最大顶点数
vector<T> vertexList; //顶点表
vector< vector<int> > edge;//邻接矩阵
//int currentEdges; //当前边数
bool findVertex(const T& v);
int getVertexPosition(const T& v);
public:
// Graph();
bool isEmpty();
void printGraph();
int getNumberOfVertex();
int getWeight(const T& v1, const T& v2);
int getFirstNeighbor(int v);
int getNextNeighbor(int v1,int v2);
void insertVertex(const T& v);
void deleteVertex(const T& v);
void insertEdge(const T& v1, const T& v2, int w);
void deleteEdge(const T& v1, const T& v2);
void readGraph(string fileName);
};
//以矩阵的形式输出图
template <typename T>
void Graph<T>:: printGraph()
{
for(int i=0; i<vertexList.size(); i++)
{
cout<<vertexList[i]<<": [ ";
for(int j=0; j<vertexList.size(); j++)
{
if(edge[i][j] < INT_MAX)
cout<<edge[i][j]<<" ";
else
cout<<" ∞"<<" ";
}
cout<<"]"<<endl;
}
cout<<endl<<endl;
}
//检测图是否为空
template <typename T>
bool Graph<T>:: isEmpty()
{
return vertexList.empty();
}
//返回图的顶点个数
template <typename T>
int Graph<T>:: getNumberOfVertex()
{
return vertexList.size();
}
//返回顶点vertex在顶点表中的位置(序号)
template <typename T>
int Graph<T>:: getVertexPosition(const T& v)
{
for(int i=0; i<vertexList.size(); i++)
{
if(vertexList[i] == v)//
{
return i;
}
}
return -1;
}
//检查顶点vertex是否已在顶点表中
template <typename T>
bool Graph<T>:: findVertex(const T& v)
{
if(getVertexPosition(v) != -1)
{
return true;
}
return false;
}
//返回指定边的权值
template <typename T>
int Graph<T>:: getWeight(const T& v1, const T& v2)
{
int pos1 = getVertexPosition(v1);
int pos2 = getVertexPosition(v2);
if(pos1 != -1 && pos2 != -1)
return edge[pos1][pos2];
else
return INT_MAX;//实际应该进行异常处理
}
//返回序号为v的顶点的第一个邻接顶点的序号
template <typename T>
int Graph<T>:: getFirstNeighbor(int v)
{
for(int i=0; i<vertexList.size(); i++)
{
if(edge[v][i] > 0 && edge[v][i] < INT_MAX)
{
return i;
}
}
return -1;
}
//返回序号为v1的顶点相对于序号为v2的顶点的下一个邻接顶点的序号
template <typename T>
int Graph<T>:: getNextNeighbor(int v1,int v2)
{
if(v1 != -1 && v2 != -1)
{
for(int i = v2+1; i<vertexList.size(); i++)
{
if(edge[v1][i] > 0 && edge[v1][i] < INT_MAX)
{
return i;
}
}
}
return -1;
}
//插入一个顶点
template <typename T>
void Graph<T>:: insertVertex(const T& v)
{
if(findVertex(v))//已存在该顶点
return;
//增加列
for(int i=0; i<vertexList.size(); i++)
{
edge[i].push_back(INT_MAX);
}
//增加行
vector<int> column(vertexList.size(), INT_MAX);
column.push_back(0);
edge.push_back(column);
//加入顶点表
vertexList.push_back(v);
}
/*在图中删去顶点v和所有与它相关联的边
*删除第pos行和第pos列
*然后从顶点表中删除顶点
*/
template <typename T>
void Graph<T>:: deleteVertex(const T& v)
{
int pos = getVertexPosition(v);
if(pos == -1)
return;
//删除第pos列
for(int i=0; i<vertexList.size(); i++)
{
edge[i].erase(edge[i].begin() + pos);
}
//删除第pos行
edge.erase(edge.begin() + pos);
//从顶点表中删除
vertexList.erase(vertexList.begin() + pos);
}
/*插入一条边(v1,v2),边权值为w
*插入之前顶点已存在
*/
template <typename T>
void Graph<T>:: insertEdge(const T& v1, const T& v2, int w)
{
int pos1 = getVertexPosition(v1);
int pos2 = getVertexPosition(v2);
if(pos1 != -1 && pos2 != -1)
{
edge[pos1][pos2] = w;
edge[pos2][pos1] = w;
}
}
/*在图中删去边
*与删除的边关联的两个顶点已存在
*/
template <typename T>
void Graph<T>:: deleteEdge(const T& v1, const T& v2)
{
if(v1 == v2)
return;
int pos1 = getVertexPosition(v1);
int pos2 = getVertexPosition(v2);
if(pos1 != -1 && pos2 != -1)
{
edge[pos1][pos2] = INT_MAX;
edge[pos2][pos1] = INT_MAX;
}
}
//从文件中的数据构造图
template <typename T>
void Graph<T>:: readGraph(string fileName)
{
int vertexNumber;
T vertex;
T v1,v2;
int weight;
ifstream in(fileName.c_str(),ios_base::in);
if(!in)
{
cout<<"file open error!"<<endl;
exit(1);
}
in>>vertexNumber;
for(int i=0; i<vertexNumber; i++)
{
in>>vertex;
insertVertex(vertex);//插入顶点
}
while(in>>v1>>v2>>weight)
{
insertEdge(v1, v2, weight);//插入边
}
}
(2) 测试程序
#include <cstdlib>
#include <iostream>
#include <string>
#include "graph.h"
int main(int argc, char *argv[])
{
Graph<string> g;
string fileName = "graph.txt";
g.readGraph(fileName);
// //插入顶点
// g.insertVertex("beijing");
// g.insertVertex("shanghai");
// g.insertVertex("guangzhou");
// g.insertVertex("shenzhen");
// //插入边
// g.insertEdge("beijing", "shanghai", 1000);
// g.insertEdge("beijing", "guangzhou", 2000);
// g.insertEdge("beijing", "shenzhen", 2100);
// g.insertEdge("shanghai", "guangzhou", 500);
// g.insertEdge("shanghai", "shenzhen", 600);
// g.insertEdge("guangzhou", "shenzhen", 100);
cout<<"after insert vertex and edges: "<<endl;
g.printGraph();
//删除边
g.deleteEdge("beijing", "shenzhen");
g.deleteEdge("shanghai", "shenzhen");
cout<<"after delete edges: "<<endl;
g.printGraph();
//删除顶点
// g.deleteVertex("shanghai");
g.deleteVertex("shenzhen");
cout<<"after delete vertex: "<<endl;
g.printGraph();
system("PAUSE");
return EXIT_SUCCESS;
}
注:
(1)图描述的是北上广深城市之间的距离,权值是随便写的,可以无视。
(2)graph.txt内容
4
beijing shanghai guangzhou shenzhen
beijing shanghai 1000
beijing guangzhou 2000
beijing shenzhen 2100
shanghai guangzhou 500
shanghai shenzhen 600
guangzhou shenzhen 100
参考资料:
[1]《数据结构》刘大有 唐海鹰等著 高等教育出版社