1、邻接矩阵
用一个一维数组存储图中顶点的信息
用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。
- 无向图的邻接矩阵:主对角线为 0 且一定是对称矩阵。
顶点i的度:邻接矩阵的第i行(或第i列)非零元素的个数。
判断顶点 i 和 j 之间是否存在边:测试邻接矩阵中相应位置的元素arc[i][j]是否为1。
求顶点 i 的所有邻接点:将数组中第 i 行元素扫描一遍,若arc[i][j]为1,则顶点 j 为顶点 i 的邻接点。 - 有向图的邻接矩阵不一定对称。
求顶点 i 的出度:邻接矩阵的第 i 行元素之和。
求顶点 i 的入度:邻接矩阵的第 i 列元素之和。
判断从顶点 i 到顶点 j 是否存在边:测试邻接矩阵中相应位置的元素arc[i][j]是否为1。
2、邻接矩阵存储无向图的类
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;
};
- 构造函数
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; //置有边标志
}
}
- 深度优先遍历
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 );
}
- 广度优先搜索
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;
}
}
}
- 增加一个顶点
在存储顶点的一维数组中插入该顶点的信息
在邻接矩阵中插入一行、一列
template <class T>
void MGraph<T>::InsertVex(int num,T name) {
if ( num<0|| num>vertexNum) throw "位置";
int row, col, numv;
numv = vertexNum-1;
vertexNum++;
for(int i=numv;i>=num;i--) vertex[i++]=vertex[i];
vertex[num]=name;
for(row=numv;row>=0;row--) {所有行上num列之后的列后移,增加一列,
for(col=numv;col>=num;col--) arc[row][col+1]=arc[row][col];
arc[row][num]=0;
}
for(row=numv;row>=num;row--)
for(col=0;col<=numv+1;col++) arc[row+1][col]=arc[row][col];
for(col=0;col<vertexNum;col++) arc[num][col]=0;
}
- 删除一个顶点
在存储顶点的一维数组中删除该顶点的信息
在邻接矩阵中删除一行、一列
template <class T> void MGraph<T>::DeleteVex(int pos){
if ( pos<0|| pos>MaxSize) throw "位置";
int row, col;
int numv=vertexNum;
for(int i=pos;i<numv;i++) vertex[i]=vertex[i+1];
vertexNum--;
for(row=0;row<numv;row++) { //删除一列
for(col=pos;col<numv;col++) arc[row][col]=arc[row][col+1];
}
for(row=pos;row<numv;row++)
for(col=0;col<numv;col++)
arc[row][col]=arc[row+1][col];
}
}
- 增加一条边
修改相应的矩阵元素的值
tmplate <class T>
void MGraph<T>::InsertArc(int i, int j)
{
if ( i>MaxSize|| j>MaxSize) throw "位置";
arc[i][j]=1;
arc[j][i]=1;
}
- 删除一条边
修改相应的矩阵元素的值
template <class T>
void MGraph<T>::DeleteArc(int i, int j)
{
if ( i>MaxSize|| j>MaxSize) throw "位置";
arc[i][j]=arc[j][i]=0;
}
3、邻接表存储的基本思想:
对于图的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,称为顶点vi的边表(对于有向图则称为出边表)
所有边表的头指针和存储顶点信息的一维数组构成了顶点表。
- 邻接表有两种结点结构:顶点表结点和边表结点。
vertex:数据域,存放顶点信息。
firstedge:指针域,指向边表中第一个结点。
adjvex:邻接点域,边的终点在顶点表中的下标。
next:指针域,指向边表中的下一个结点。
定义邻接表的结点:
struct ArcNode{
int adjvex;
ArcNode *next;
};
template <class T>
struct VertexNode{
T vertex;
ArcNode *firstedge;
};
- 无向图的邻接表
求顶点 i 的度:顶点i的边表中结点的个数。
判断顶点 i 和顶点 j之间是否存在边:测试顶点 i 的边表中是否存在终点为 j 的结点。 - 有向图的邻接表(出边表)
求顶点 i 的出度:顶点 i 的出边表中结点的个数。
求顶点 i 的入度:各顶点的出边表中以顶点 i 为终点的结点个数。
求顶点 i 的所有邻接点:遍历顶点 i 的边表,该边表中的所有终点都是顶点 i 的邻接点。 - 有向图的逆邻接表(入边表)
4、邻接表存储有向图的类
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;
};
- 构造函数
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;
}
}
}
- 增删顶点
增加:顶点表中插入一个元素
删除:在顶点表中删除一个元素,同时在边表中删除相应的边 - 增删边<x, y>
如果是有向图,则在x的边表中增加/删除边;
如果是无向图,则还要在y的边表中增加/删除一条边
5、图的存储结构
邻接矩阵
有向图和无向图
邻接表
有向图(出边表)和无向图
逆邻接表(有向图的入边表)
有向图,方便计算顶点的入度
有向图的十字链表
无向图的邻接多重表
边集数组
6、十字链表:有向图的链式存储结构 。将邻接表与逆邻接表合二为一。
本质是将在有向图的邻接表和逆邻接表中两次出现的同一条弧用一个结点表示 。
十字链表的结点结构:
vertex:数据域,存放顶点信息;
firstin:入边表头指针;
firstout:出边表头指针;
tailvex:弧的起点在顶点表中的下标;
headvex:弧的终点在顶点表中的下标;
headlink:入边表指针域;
taillink:出边表指针域。
7、邻接多重表 :无向图的存储结构。
8、边集数组
利用两个一维数组
一个数组存储顶点信息,
··另外一个数组存储边及其权
··数组分量包含三个域:边所依附的两个顶点,权值
··各边在数组中的次序可以任意。
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++;
}
}
9、图的存储结构的比较——邻接矩阵和邻接表