定定义
2特点
无向图的邻接
矩阵一定是对称的,而有向图的邻接矩阵不一定对称。因此,用邻接
矩阵来表示一个具有n个顶点的有向图时需要n^2个单元来存储邻接矩阵;对有n个顶点的
无向图则只存入上(下)三角阵中剔除了左上右下对角线上的0元素后剩余的元素,故只需1+2+...+(n-1)=n(n-1)/2个单元。
无向图邻接
矩阵的第i行(或第i列)非零元素的个数正好是第i个顶点的度。
以上内容来自度娘,基本上讲清楚了图的基本概念,接下就实现之:
Graph.h
#ifndef GRAPH_H
#define GRAPH_H
#define UNVISITED 0
#define VISITED 1
#define INFINITE 0xffffffff
//Edge类
class Edge
{
public://公开为public是为了算法的方便,实际工作应用中应为private
int weight;//weight是边的权
int to;//to是边的终点
int from;//from是边的始点
public:
Edge()
{
weight = 0;
to = -1;
from = -1;
}
Edge(int w,int t,int f) : weight(w),to(t),from(f)
{
}
bool operator < (const Edge &edge)
{
return this->weight < edge.weight;
}
bool operator > (const Edge &edge)
{
return this->weight > edge.weight;
}
bool operator == (const Edge &edge)
{
return this->weight == edge.weight;
}
bool operator <= (const Edge &edge)
{
return this->weight <= edge.weight;
}
bool operator >= (const Edge &edge)
{
return this->weight >= edge.weight;
}
};
class Graph
{
public:
int numVertex; //图的顶点的个数
int numEdge; //图的边的数目
int *mark; /*Mark指针指向保存有图的顶点的标志位的数组,标志位用来标记某顶点是否被访问过*/
int *indegree; //Indegree指针指向保存有图的顶点的入度的数组
Graph(int numVert)
{ //构造函数
numVertex = numVert; //确定图的顶点的个数
numEdge = 0; //确定图的边的数目
indegree = new int[numVertex]; /*为保存图的顶点的入度申请数组,indegree为数组指针*/
mark = new int[numVertex]; /*为图的顶点的标志位申请数组,Mark为数组指针*/
for (int i = 0; i < numVertex; i++) { /*确定图的顶点的标志位和入度,即所有顶点的标志位初始化为未被访问过,入度初始化为0*/
mark[i] = UNVISITED;
indegree[i] = 0;
}
}
~Graph()
{
delete []mark;
delete []indegree;
}
virtual Edge firstEdge(int oneVertex)// 返回与顶点oneVertex相关联的第一条边
{
Edge edge;
edge.from = oneVertex;
edge.to = -1;
return edge;
}
virtual Edge nextEdge(Edge preEdge)// 返回与边PreEdge有相同关联顶点的下一条边
{
return preEdge;
}
int verticesNum() //返回图的顶点个数
{
return numVertex;
}
int edgesNum() //返回图的边数
{
return numEdge;
}
int fromVertex(Edge oneEdge) // 返回oneEdge的始点
{
return oneEdge.from;
}
int toVertex(Edge oneEdge) // 返回oneEdge的终点
{
return oneEdge.to;
}
int weight(Edge oneEdge) // 返回oneEdge的权值
{
return oneEdge.weight;
}
bool isEdge(Edge oneEdge)//如果oneEdge是边则返回TRUE,否则返回FALSE
{
if(oneEdge.weight > 0 && oneEdge.weight < INFINITE && oneEdge.to >= 0)
{
return true;
}
else
{
return false;
}
}
virtual void setEdge(int from, int to, int weight) = 0;
virtual void delEdge(int from, int to) = 0;
};
#endif
GraphMatrix.h
#include "Graph.h"
#include <iostream>
#include <queue>
using std::cout;
using std::cin;
using std::endl;
using std::queue;
#define N 5 // 定义图的顶点数
class GraphMatrix : public Graph
{
int ** matrix; //指向相邻矩阵的指针
public:
GraphMatrix(int numVert) : Graph(numVert)
{
int i, j; //i, j作为for循环中的计数器
matrix = (int **)new int*[numVertex]; /*申请matrix数组,该数组有numVertex个元素,每个元素是整型指针类型*/
for (i = 0; i < numVertex; i ++) /*matrix数组的每个元素,都指向一个具有numVertex个元素的数组*/
matrix[i] = new int[numVertex];
for (i = 0; i < numVertex; i++) /*相邻矩阵的所有元素都初始化为0,如果矩阵元素matrix[i][j]不为0,则表明顶点i与顶点j之间有一条边,边的权即为matrix[i][j]的值*/
for (j = 0; j < numVertex; j ++)
matrix[i][j] = 0;
}
~GraphMatrix()
{
for(int i = 0; i < numVertex; i++)
{
delete [] matrix[i];//释放每个matrix[i]申请的空间
}
delete [] matrix;//释放matrix指针指向的空间
}
virtual Edge firstEdge(int oneVertex)// 返回与顶点oneVertex相关联的第一条边
{
Edge edge;
edge.from = oneVertex;//将顶点oneVertex作为边edge的始点
edge.to = -1;
/* 下面寻找第一个使得matrix[oneVertex][i]
不为0的i值,那么边(oneVertex,i)或者
弧<oneVertex,i>即为顶点oneVertex
的第一条边,将顶点i作为边medge的终点边edge
的权为矩阵元素matrix[oneVertex][i]的值*/
for(int i = 0; i < numVertex; i++)
{
if(matrix[oneVertex][i] != 0)
{
edge.to = i;
edge.weight = matrix[oneVertex][i];
break;
}
}
return edge;
}
virtual Edge nextEdge(Edge preEdge)// 返回与边PreEdge有相同关联顶点的下一条边
{
Edge edge;
edge.from = preEdge.from;
edge.to = -1;
//如果preEdge.to+1>=numVertex,那么就不存在下一条边了
if(preEdge.to < numVertex)
{
for(int i = preEdge.to + 1; i < numVertex; i++)
{
/*寻找下一个使得//matrix[preEdge.from][i]不为0的i值,那么
(preEdge.from,i)或者<preEdge.from,i>即为顶点preEdge.from的下一条边*/
if(matrix[preEdge.from][i] != 0)
{
edge.to = i;
edge.weight = matrix[preEdge.from][i];
break;
}
}
}
/*如果找到了顶点preEdge.from的下一条边,则返回的edge确实是一条边;
如果没有找到顶点preEdge.from的下一条边,则edge的成员变量to为-1,
根据IsEdge函数判断可知edge不是一条边*/
return edge;
}
void setEdge(int from, int to, int weight)
{
/*如果matrix[from][to]<=0,则边(from,to) 或者<from,to>
将是新增的一条边,否则该边已经存在(现在只是对该边进行修改)*/
if(matrix[from][to] <= 0)
{
numEdge++;
indegree[to]++;
}
matrix[from][to] = weight;
}
void delEdge(int from, int to)
{
/*如果matrix[from][to]>0,则边(from,to)或者<from,to>确实存在,
否则该边实际上并不存在(从而不必对图的边数和顶点to的入度进行修改)*/
if(matrix[from][to] > 0)
{
numEdge--;
indegree[to]--;
}
matrix[from][to] = 0;
}
// 函数功能:初始化图
void initGraph(GraphMatrix &graphM,int (*A)[N],int n)
{
for (int i = 0; i < n; i ++)
{
for (int j = 0; j < N; j ++)
{
if (A[i][j] > 0)
graphM.setEdge(i, j, A[i][j]);
}
}
}
void DFS( GraphMatrix &graph,int vertex)
{
graph.mark[vertex] = VISITED;
visit(graph,vertex);
for(Edge edge = graph.firstEdge(vertex); graph.isEdge(edge); edge = graph.nextEdge(edge))
{
if(graph.mark[graph.toVertex(edge)] == UNVISITED)
{
DFS(graph,graph.toVertex(edge));
}
}
}
void BFS( GraphMatrix &graph,int vertex)
{
queue<int> q; // 初始化广度优先周游要用到的队列
q.push(vertex);
while(!q.empty())
{
int v = q.front();
q.pop();
if(graph.mark[v] == UNVISITED )
{
visit(graph,v);
graph.mark[v] = VISITED;//标记该顶点已访问
// 该顶点邻接到的每一个未访问顶点都入队
for(Edge edge = graph.firstEdge(v); graph.isEdge(edge); edge = graph.nextEdge(edge))
{
if(graph.mark[graph.toVertex(edge)] == UNVISITED)
{
q.push(graph.toVertex(edge));
}
}
}
}
}
void visit(const GraphMatrix &graph,int vertex)
{
cout << "V:" << vertex << "\t";
}
};
测试代码:
#include "GraphMatrix.h"
#include <iostream>
int A[N][N] = {
// V0 V1 V2 V3 V4
/*V0*/ 0, 0, 1, 1, 0,
/*V1*/ 0, 0, 0, 1, 1,
/*V2*/ 1, 0, 0, 1, 1,
/*V3*/ 1, 1, 1, 0, 0,
/*V4*/ 0, 1, 1, 0, 0,}; //北大张铭《数据结构与算法》图7.2中G1表示的无向图
int main()
{
GraphMatrix graphMatrix(N); // 建立图
graphMatrix.initGraph(graphMatrix, A,N); // 初始化图
cout << "DFS: ";
graphMatrix.DFS(graphMatrix, 0);
cout << endl;
for (int i = 0; i < graphMatrix.verticesNum(); i++) //把Mark改回UNVISITED
graphMatrix.mark[i] = UNVISITED;
cout << "BFS: ";
graphMatrix.BFS(graphMatrix, 0);
cout << endl;
system("pause");
return 0;
}