一.邻接矩阵的空间复杂度
假设图G有n个顶点e条边,则存储该图需要O(n^2)
不适用稀疏图的存储
二.邻接表
1.邻接表的存储思想:
对于图的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,称为顶点vi的边表(对于有向图则称为出边表),所有边表的头指针和存储顶点信息的一维数组构成了顶点表。
2.邻接表的结构定义
struct ArcNode{
int adjvex;
struct ArcNode *next;
};
template <class T>
struct VertexNode{
T vertex;
struct ArcNode *firstEdge;
};
邻接表的空间复杂度为O(n+e)
更适用于有向图的存储
3.无向图的邻接表
1.如何求顶点i的度?
顶点i的边表中结点的个数
2.如何判断顶点vi和vj之间是否有边?
测试顶点vi的边表中是否有终点为j的结点
4.有向图的邻接表
1.如何求顶点i的出度?
顶点i的边表中结点的个数
2.如何求顶点i的入度?
各顶点的出边表中以顶点i为终点的结点个数
5.网图的邻接表
三.邻接表存储有向图的类
struct ArcNode{
int adjvex;
struct ArcNode *next;
};
template <class T>
struct VertexNode{
T vertex;
struct ArcNode *firstEdge;
};
template <class T>
class ALGraph{
private:
VertexNode<T> adjList[MAX_VERTEX];
int vertexNum,arcNum;
public:
ALGraph(T v[],int n,int e);
~ALGraph();
void DFSTraverse();
void BFSTraverse();
};
template <class T>
ALGraph<T>::ALGraph(T v[],int n,int e){
int vi,vj;
ArcNode *s;
vertexNum=n;
arcNum=e;
for(int i=0;i<n;i++){
adjList[i].vertex=v[i];
adjList[i].firstEdge=NULL;
}
for(int i=0;i<arcNum;i++){
cin>>vi>>vj;
s=new ArcNode;
s->adjvex=vj;
s->next=adjList[vi].firstEdge;
adjList[vi].firstEdge=s;
}
}
template <class T>
ALGraph<T>::~ALGraph(){
int i,j;
ArcNode *p;
for(i=0;i<vertexNum;i++){
p=adjList[i].firstEdge;
if(p){
while(p){
adjList[i].firstEdge=p->next;
delete p;
p=adjList[i].firstEdge;
}
}
}
}
四.逆邻接表
对于邻接表来说,查找入度非常困难。
所以引入了逆邻接表。
五.十字链表 (有向图)
十字链表是将有向图的邻接表和逆邻接表结合起来的一种有向图链式存储结构。
十字链表的结点结构:
data | firstin | firstout |
data:存放顶点的有关信息
firstin:指向以该顶点为弧头的第一个弧结点
firstout:指向以该顶点为弧尾的第一个弧结点
tailvex | headvex | hlink | tlink |
tailvex:指示该弧的弧尾顶点
headvex:指示该弧的弧头顶点
hlink:指向弧头相同的下一条弧
tlink:指向弧尾相同的下一条弧
通过hlink将弧头相同的弧连在一个链表上
通过tlink将弧尾相同的弧连在一个链表上
十字链表求入度:
从顶点vi的firstin出发,沿着弧结点中的hlink所经过的弧结点数。
十字链表求出度:
从顶点vi的firstout出发,沿着弧结点中的tlink所经过的弧结点数。
六.邻接多重表(无向图)
解决了无向图边记录两次的问题
data | firstedge |
ivex | ilink | jvex | jlink |
- ivex和jvex为该边所依附的两个顶点
- ilink指向下一条依附于ivex的边
- jlink指向下一条依附于jvex的边
- data存放顶点的信息
- firstedge指向第一条依附于该顶点的边结点
邻接多重表的特点:
- 顶点结点数为n,边结点数为e;
- 一个边同时被两个顶点结点对应的链表复用。