邻接矩阵(数组表示法)
基本思想: 用一个一维数组存储图中顶点的信息,用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。
无向图的邻接矩阵
1、无向图的邻接矩阵的特点:
主对角线为0且一定是对称矩阵。
2、如何求顶点i的度?
邻接矩阵的第i行(或第i列)非零元素的个数。
3、如何判断顶点 i 和 j 之间是否存在边?
测试邻接矩阵中相应位置的元素arc[i][j]是否为1。
4、如何求顶点 i 的所有邻接点?
将数组中第 i 行元素扫描一遍,若arc[i][j]为1,则顶点 j 为顶点 i 的邻接点。
有向图的邻接矩阵
1、有向图的邻接矩阵一定不对称吗?
不一定,例如有向完全图。
2、如何求顶点 i 的出度?
邻接矩阵的第 i 行元素之和。
3、如何求顶点 i 的入度?
邻接矩阵的第 i 列元素之和。
4、如何判断从顶点 i 到顶点 j 是否存在边?
测试邻接矩阵中相应位置的元素arc[i][j]是否为1。
邻接矩阵存储无向图的类
const int MaxSize=10;
template<class T>
class Mgraph{
public:
MGraph(T a[ ], int n, int e );
~MGraph( )
void DFSTraverse(int v);
void BFSTraverse(int v);
……
private:
T vertex[MaxSize];
int arc[MaxSize][MaxSize];
int vertexNum, arcNum;
};
邻接矩阵中图的基本操作——构造函数
MGraph(T a[ ], int n, int e );
1、确定图的顶点个数和边的个数;
2、输入顶点信息存储在一维数组vertex中;
3、初始化邻接矩阵;
4、依次输入每条边存储在邻接矩阵arc中;
4.1 输入边依附的两个顶点的序号i, j;
4.2 将邻接矩阵的第i行第j列的元素值置为1;
4.3 将邻接矩阵的第j行第i列的元素值置为1;
template<class T>
MGraph::MGraph(T a[ ], int n, int e) {
vertexNum=n; arcNum=e;
for (i=0; i<vertexNum; i++)
vertex[i]=a[i];
for (i=0; i<vertexNum; i++)
for (j=0; j<vertexNum; j++)
arc[i][j]=0;
for (k=0; k<arcNum; k++) {
cin>>i>>j;
arc[i][j]=1;
arc[j][i]=1;
}
}
邻接矩阵中图的基本操作——深度优先遍历
⑴ 访问顶点v;
⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。
递归定义
int visited[MaxSize];
template<class T>
void MGraph::DFSTraverse(int v){
cout<<vertex[v]; visited [v]=1;
for (j=0; j<vertexNum; j++)
if (arc[v][j]==1 && visited[j]==0)
DFSTraverse( j );
}
邻接矩阵中图的基本操作——广度优先遍历
⑴ 访问顶点v;
⑵ 依次访问v的各个未被访问的邻接点v1, v2, …, vk;
⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。
int visited[MaxSize];
template<class T>
void MGraph::BFSTraverse(int v){
front=rear=-1;
int Q[MaxSize];
cout<<vertex[v];
visited[v]=1;
Q[++rear]=v;
while (front!=rear){
v=Q[++front];
for (j=0; j<vertexNum; j++)
if (arc[v][j]==1 && visited[j]==0 ){
cout<<vertex[j];
visited[j]=1;
Q[++rear]=j;
}
}
}
邻接矩阵上的其他操作
增加一个顶点
在存储顶点的一维数组中插入该顶点的信息,在邻接矩阵中插入一行、一列。
删除一个顶点
在存储顶点的一维数组中删除该顶点的信息,在邻接矩阵中删除一行、一列。
增加一条边
修改相应的矩阵元素的值。
删除一条边
修改相应的矩阵元素的值。
邻接表
假设图G有n个顶点e条边,则存储该图需要O(n2) 。
基本思想: 对于图的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,称为顶点vi的边表(对于有向图则称为出边表),所有边表的头指针和存储顶点信息的一维数组构成了顶点表。
邻接表有两种结点结构:顶点表结点和边表结点。
定义邻接表的结点
struct ArcNode{
int adjvex;
ArcNode *next;
};
template<class T>
struct VertexNode{
T vertex;
ArcNode *firstedge;
};
无向图的邻接表
1、边表中的结点表示什么:
每个结点对应图中的一条边, 邻接表的空间复杂度为O(n+e)。
2、如何求顶点 i 的度?
顶点i的边表中结点的个数。
3、如何判断顶点 i 和顶点 j 之间是否存在边?
测试顶点 i 的边表中是否存在终点为 j 的结点。
有向图的邻接表(出边表)
1、如何求顶点 i 的出度?
顶点 i 的出边表中结点的个数。
2、如何求顶点 i 的入度?
各顶点的出边表中以顶点 i 为 终点的结点个数。
3、如何求顶点 i 的所有邻接点?
遍历顶点 i 的边表,该边表中的所有终点都是顶点 i 的邻接点。
有向图的逆邻接表(入边表)
为方便的计算有向图的顶点的入度,可以构造逆邻接表。 在逆邻接表中,边表中存储的是以顶点vi为弧头的弧。
邻接表存储有向图的类
const int MaxSize=10;
template<class T>
class ALGraph {
public:
ALGraph(T a[ ], int n, int e);
~ALGraph;
void DFSTraverse(int v);
void BFSTraverse(int v);
………
private:
VertexNode adjlist[MaxSize];
int vertexNum, arcNum;
};
邻接表中图的基本操作——构造函数
1、确定图的顶点个数和边的个数;
2、输入顶点信息,初始化该顶点的边表;
3、依次输入边的信息并存储在边表中;
3.1 输入边所依附的两个顶点的序号i和j;
3.2 生成邻接点序号为j的边表结点s;
3.3 将结点s插入到第i个边表的头部;
template<class T>
ALGraph::ALGraph(T a[ ], int n, int e) {
vertexNum=n;
arcNum=e;
for (i=0; i<vertexNum; i++)
{
adjlist[i].vertex=a[i];
adjlist[i].firstedge=NULL;
}
for (k=0; k<arcNum; k++)
{
cin>>i>>j;
s=new ArcNode;
s->adjvex=j;
s->next=adjlist[i].firstedge;
adjlist[i].firstedge=s;
}
}
邻接表中图的基本操作——深度优先遍历
template<class T>
void ALGraph::DFSTraverse(int v){
cout<<adjlist[v].vertex;
visited[v]=1;
p=adjlist[v].firstedge;
while (p!=NULL){
j=p->adjvex;
if (visited[j]==0)
DFSTraverse(j);
p=p->next;
}
}
邻接表中图的基本操作——广度优先遍历
template<class T>
void ALGraph::BFSTraverse(int v){
front=rear=-1;
cout<<adjlist[v].vertex;
visited[v]=1;
Q[++rear]=v;
while (front!=rear){
v=Q[++front];
p=adjlist[v].firstedge;
while (p!=NULL){
j= p->adjvex;
if (visited[j]==0){
cout<<adjlist[j].vertex;
visited[j]=1;
Q[++rear]=j;
}
p=p->next;
}
}
}
插入边
template <class T>
void ALGraph<T>::InsertArc(int i, int j)
{
if ( i>MaxSize || j>MaxSize)
throw "位置";
ArcNode *s=new ArcNode;
s->adjvex=j;
s->next=adjlist[i].firstedge;
adjlist[i].firstedge=s;
}
删除边
template <class T>
void ALGraph<T>::DeleteArc(int i, int j)
{
if ( i>MaxSize|| j>MaxSize)
throw "位置";
ArcNode *s;
ArcNode *pre;
s = adjlist[i].firstedge;
if (!s)
return;
if(s->adjvex==j)
{
adjlist[i].firstedge=s->next;
return;
}
pre = adjlist[i].firstedge;
while(s!=NULL && s->adjvex!=j)
{
pre = s;
s = s->next;
}
if(s!=NULL)
{
pre->next = s->next;
delete s;
}
}
插入顶点
template <class T>
void ALGraph<T>::InsertVex( T value)
{
if ( i>vertexNum || i<0 || i>MaxSize )
throw "顶点位置异常";
vertexNum++;
VertexNode<T> tempvertex;
tempvertex.vertex = value;
tempvertex.firstedge = NULL;
adjlist[VertexNum-1] = tempvertex;
}
边集数组的实现
Struct edge
{
int i;
int j;
int weight;
}
将邻接矩阵转化成边集数组
edge edges[M];//边的数据结构类型的变量
for ( i = 0; i < G->vexnum; i++) {
for (j = 0; j <= G->vexnum; j++) {
if (G->arc[i][j] == 1) {
edges[k].begin = i;
edges[k].end = j;
// edges[k].weight = G->arc[i][j];
k++;
}
}
}