邻接矩阵:
邻接矩阵是用矩阵来表示顶点之间的相邻关系的一种方法。A[i,j]=1则是图的边。若A[i,j]=0,则不是图的边。若G是网络则A[i,j] = wij; wij表示边上的权值,若A[i,j]=0或无穷则计算机允许的大于所有边上权值的数。
邻接矩阵表示带权图类:
除了采用二维数组存储用于表示顶点间相邻关系的邻接矩阵外,通常还需要用一个顺序表来存储顶点信息。
vertexlist是一个顺序表,存储图的顶点集和,adjmatrix是一个动态二维数组,存储图的邻接矩阵,size表示二维数组容量,vertCount表示图的顶点数
#include<iostream>
using namespace std;
struct Edge //带权值的边
{
int start; //边的起点
int dest; // 边的终点
int weight; //边的权值
friend ostream& operator<<(ostream& out,Edge &e);
};
ostream& operator <<(ostream& out,Edge &e)
{
out<<"("<<e.start<<","<<e.dest<<","<<e.weight<<")";
return out;
}
#include "SeqQueue.h"
template<class T>
class AbstractGraph //抽象图类
{
public:
virtual int VertexCount() = 0; //返回顶点数,纯虚构函数,由子类实现
virtual T Get(int i) = 0; //返回顶点vi的数据域
virtual int GetFirstNeighbor (int v) = 0; // 返回顶点v的第一个邻接顶点的序号
virtual int GetNextNeighbor(int v, int w) = 0; //返回顶点v在w后的下一个邻接顶点的序号
void DFSTraverse(int v); //从顶点v出发对非连通图进行一次深度优先搜索遍历
void BFSTraverse(int v); // 从顶点v出发对非连通图进行一次广度优先搜索遍历
private:
void depthfs(int v,bool visited[]); //从顶点v出发的一次深度优先搜索遍历
void breadthfs(int v,bool visited[]); //从顶点v出发的一次广度优先搜索遍历
};
#include"Edge.h" // 边类
#include"SequenceList.h" //顺序表类
#include"AbstractGraph.h" //抽象图类
template<class T>
class AdjMatrixGraph:public AbstractGraph<T> //邻接矩阵表示的带权图类,继承抽象图类
{
private:
SequenceList<T> vertexlist; //顺序表存储图的顶点集和,已执行构造函数
int **adjmatrix; // 图的邻接矩阵,动态二维数组
int size; // 二维数组的容量
int vertcount; //顶点数,vertCount<=size
void Initial(int size); //初始化图的邻接矩阵表示
public:
AdjMatrixGraph(int size = 10); //构造方法,size指定二维数组的容量
AdjMatrixGraph(T vertices [],int vertcount,Edge edges[],int edgeCounT); //以顶点集和和边集和构造一个图
~AdjMatrixGraph ();
int VertexCount(); //返回顶点数
T Get(int i); //返回顶点vi的数据元素
void InsertEdge(int i,int j,int weight); //插入一条权值为weight的边
bool InsertEdge(Edge edge); //插入一条边
friend ostream& operator <<(ostream& out,AdjMatrixGraph<T> &graph); //输出图的顶点集合和邻接矩阵
bool RemoveEdge(int i,int j); //删除边指定顶点序号
bool RemoveVertex(int v, T &old); //删除序号为v的顶点及其相关联的边
int GetFristNeighbor(int v); //返回顶点v的第一个邻接顶点的序号
int GetNextNeighbor(int v,int w); // 返回v在w后的下一个邻接顶点的符号
void MinSpanTree_prim(Edge *mst); //构造带权图最小生成树的普里姆算法
void ShortestPath(int v); //以Dijkstra算法求带权图中顶点的单源最短路径
void ShortestPa(); //以Floyd算法求带权图中每对顶点之间的最短路径
};
const int MAX_WEIGHT = 9999;// 最大权值
带权值的边结构体:
#include<iostream>
using namespace std;
struct Edge //带权值的边
{
int start; //边的起点
int dest; // 边的终点
int weight; //边的权值
friend ostream& operator<<(ostream& out,Edge &e);
};
ostream& operator <<(ostream& out,Edge &e)
{
out<<"("<<e.start<<","<<e.dest<<","<<e.weight<<")";
return out;
}
图的构造和析构函数:
template<class T>
AdjMatrixGraph<T>::~AdjMatrixGraph() //顺序表vertexlist析构已由SeqList类实现
{
for(int i=0;i<size;i++) // 释放动态二维数组占用的内存空间
delete(adjmatrix[i]);
delete(adjmatrix);
}
template<class T>
int AdjMatrixGraph<T>::VertexCount() //返回顶点数
{
return vertcount;
}
template<class T>
T AdjMatrixGraph<T>::Get(int i)
{
return vertexlist.Get(i);
}
template<class T>
ostream& operator<<(ostream& out,AdjMatriGraph<T>&graph)
{
out<<"顶点集合:"<<graph.vertexlist<<"领接矩阵:\n";
int n = graph.vertcount;
for(int i = 0;i<n;i++)
{
for(int j=0;j<n;j++)
if(graph.adjmatrix[i][j] == MAX_WEIGHT)
out<<" 无穷";
else
out<<" "<<graph.adjmatrix[i][j];
out<<" \n";
}
return out;
}
图的插入操作:
插入一个顶点vertex,就是在顶点的顺序表vertexlist最后插入一个新的元素vertex,顺序表容量不足的情况下将自动扩充容量,同时根据二维数组的情况自动扩充邻接矩阵的容量。
template<class T>
void AdjMatrixGraph<T>::InsertVertex(T vertex) //插入一个顶点
{
vertexlist.insert(vertex); //在顺序表最后插入一个元素
vertcount++;
if(vertcount>size)
{
int **temp = adjmatrix;
adjmatrix = new int *[size*2]; //二叉树组容量扩充2倍
int i,j;
for(i=0;i<size;i++)
{
adjmatrix[i] = new int[size*2];
for(j = 0;j<size;j++)
adjmatrix[i][j] = temp[i][j];
for(j = size;j<size*2;j++)
adjmatrix[i][j] = MAX_WEIGHT;
}
for(i = size;i<size*2;i++) // 初始化扩充的邻接矩阵
{
adjmatrix[i] = new int[size*2];
for(j = 0;j<size*2;j++)
adjmarix[i][j] = (i==j)?0:MAX_WEIGHT;
}
size*=2;
}
}
template<class T>
bool AdjMatrixGraph<T>::InsertEdge(int i, int j, int weight) //插入一条权值为weight的边 若该边已存在,则不插入
{
if(i>=0&&i<vertcount&&j>=0&&j<vertcount&&i!=j&&adjmatrix[i][j]==MAX_WEIGHT)
{
adjmatrix[i][j] = weight;
return true;
}
return false;
}
template<class T>
bool AdjMatrixGraph<T>::InsertEdge(Edge edge) //插入一条边
{
return InsertEdge(edge.start,edge.dest,edge.weighT);
}
图的删除操作:
删除图的一个顶点,除了要在顶点顺序表中删除指定顶点,还要在邻接矩阵中删除与该顶点相关联的所有边。删除vi顶点实际上就是把该点后所有的点都往前移动一个位置,同时把邻接矩阵中,第i+1至n-1列的元素均向前移动。
删除邻接矩阵中的一条边:只要将邻接矩阵中的该边的权值设置为无穷即可
template<class T>
bool AdjMatrixGraph<T>::RemoveEdge(int i, int j) //删除边成功返回true
{
if(i>=0&&i<vertcount&&j>=0&&j<vertcount&&i!=j&&adjmatrix[i][j]!=MAX_WEIGHT)
{
adjmatrix[i][j] = MAX_WEIGHT;
return true;
}
return false;
}
删除邻接矩阵vi的顶点
template<class T>
bool AdjMatrixGraph<T>::RemoveVertex(int v, T &old)
{
if(v>=0&&v<vertcount&&vertexlist.remove(v,old))
{
for(int i=v;i<vertcount-1;i++)
for(int j=0;j<vertcount;j++)
adjmatrix[i][j] = adjmatrix[i+1][j]; //元素向前一行移动
for(int j =v;j<vertcount-1;j++)
for(int i = 0;i<vertcount-1;i++)
adjmatrix[i][j] = adjmatrix[i][j+1]; //元素向前一列移动
vertcount--;
return true;
}
return false;
}
邻接矩阵性能分析:
一个有n个顶点,e条边的图,对应的邻接矩阵使用n的平方个元素存储了e条边的信息,当n较小且e较大时,邻接矩阵的存储效率是比较高的;反之,当n较大且e远小于n平方,矩阵变成稀疏矩阵,浪费的存储空间较多。