图:G=(V,E)
V:顶点(数据元素)的有穷非空集合 E:边的有穷集合
无向图:每条边都是无方向的
有向图:每条边都是有方向的
完全图:任意两个点之间都有一条边
稀疏图:有很少边或弧的图(e < n logn)
稠密图:有较多边或弧的图
网:边/弧带权的图
邻接:有边/弧相连的两个顶点的关系
存在(vi,vj),称vi和vj互为相接点(无向图)
存在<vi,vj>,称vi邻接到vj,vj邻接于vj(有向图)
关联(依附):边.弧与顶点之间的关系
顶点的度:与该顶点相关联的边的数目,记作TD(v)
有向图中分为入度ID(v)和出度OD(v)
图的逻辑结构:多对多
图没有顺序结构,但是可以用二维数组来表示元素之间的关系,称为数组表示法(邻接矩阵)
链式存储结构:邻接表、邻接多重表、十字链表
邻接矩阵法:
建立顶点表和一个邻接矩阵
设图A = (V,E)有n个顶点,则:顶点表Vexs[n]
i | 0 | 1 | 2 | ... | n-1 |
Vex[i] | V1 | V2 | V3 | ... | Vn |
图的邻接矩阵是一个二维数组A.arcs[n][n]定义为:
A.arcs[i][j] = 1,如果 <i,j> 或 (i,j) 存在
=0,如果不存在
无向图的邻接矩阵是对称的
顶点i的度=第 i 行(列)中 1 的个数
完全图的邻接矩阵中,对角线元素为0,其余1
有向图的邻接矩阵可能不对称
顶点的出度=第 i 行元素之和
顶点的入度=第 i 列元素之和
有向网的邻接矩阵表示(将权值表现在矩阵中,无连接使用∞表示)
邻接矩阵(无向网)的建立
#define MVNum 100 //最大顶点数
typedef char VerTexType; //设顶点的数据类型为字符型
typedef int ArcType; //假设边的权值为整形
typedef struct{
VerTexType vex[MVNum]; //顶点表
ArcType arc[MVNum][MVNum]; //邻接矩阵
int vexnum,arcnum;//图当前的点数和边数
}AMGraph;
算法思路:
1.输入总顶点数和总边数
2.依次输入点的信息存入顶点表中
3.初始化邻接矩阵,使每个权值初始化为极大值
4.构造邻接矩阵
Status CreateUDN(AMGraph &G){
cin>>G.vexnuym>>G.arcnum; //输入总顶点数,总边数
for(i=0;i<G.vexnum;++i)
cin>>G.vexs[i]; //依次输入点信息
for(i=0;i<G.vexnum;++i)
for(j=0;j<G.vexnum;++j)
G.arc[i][j] = MaxInt; //边的权值均置为极大值
for(k=0;k<G.arcnum;++k){
cin>>v1>>v2>>w; //输入一条边所依附的顶点和边的权值
i=LocateVex(G,v1);
j=LocateVex(G,v2); //确定v1和v2在G中的位置
G.arcs[i][j]=w; //设定权值
G.arcs[j][i]=G.arcs[i][j]; //置对称边的权值为w
}
return OK;
}
在图中查找顶点
int LocateVex(AMGraph G,VertexType u){
int i;
for(i=0;i<G.vexnum;++i)
if(u==G.vexs[i]) return i;
return -1;
}
邻接表(链式)
顶点:按编号顺序将顶点数据存储在一维数组中
关联统一顶点的边:用线性链表存储
无向图表示↓
特点:邻接表不唯一
若无向图中有n个顶点、e调边,则需要n个表头和2e个表结点,适宜存储稀疏图
无向图中顶点Vi的度为第 i 个单链表中的结点个数
有向图表示(存储出度边 / 也可以存储入度边<逆邻接表>)
特点:顶点Vi的出度为第 i 个单链表中的结点个数
顶点Vi的入度为整个单链表中邻接借点域值是 i-1的结点个数
容易找出度,难以找入度
建立: 1.输入总顶点数和总边数
2.建立顶点表 依次输入点的信息存入顶点表中
使每个表头节点的指针域初始化为NULL
3.创建邻接表 依次输入每条边依附的两个顶点
确定两个顶点的序号 i 和 j ,建立边结点
将此边结点分别插入到Vi和Vj对应的两个边链表的头部
Status CreateUDN(AMGraph &G){
cin>>G.vexnuym>>G.arcnum; //输入总顶点数,总边数
for(i=0;i<G.vexnum;++i){
cin>>G.vertices[i].data; //依次输入点信息
G.vertices[i].firstarc=NULL; //顶点置空
}
for(k=0;k<G.arcnum;++k){
cin>>v1>>v2; //输入一条边所依附的顶点
i=LocateVex(G,v1);
j=LocateVex(G,v2); //确定v1和v2在G中的位置
//*****出度表*******//
p1 = new ArcNode(); //生成新的边结点*p1
p1->adjvex = j; //邻接点序号为j
p1->nextarc = G.vertices[i].fristarc = p1;
G.vertices[i].fristarc = p1; //将新结点*p1插入到顶点Vi的边表头部
//*****入度表*******//
p2 = new ArcNode(); //生成新的边结点*p2
p2->adjvex = j; //邻接点序号为j
p2->nextarc = G.vertices[i].fristarc = p2;
G.vertices[i].fristarc = p2; //将新结点*p2插入到顶点Vi的边表头部
}
return OK;
}
特点:方便找到任意顶点的所有邻接点
节约稀疏图的空间(需要N个头指针+2E个结点<每个结点2个域>)
方便找到有向图的度,但对于无向图需要邻接表和逆邻接表
邻接矩阵和邻接表的关系
1.邻接表中每个链表对应于邻接矩阵中的一行,链表中结点个数等于一行中非零元素的个数
2.对任意图,邻接矩阵是唯一的,但是邻接表不唯一
3.邻接矩阵的空间复杂度为O(n^2),邻接表的空间复杂度为O(n+e)
用途:邻接矩阵用于稠密图;邻接表用于稀疏图
十字链表(用于有向图)
结合邻接表和逆邻接表;有向图中每一条弧对应十字链表中的一个弧结点,每个结点对应一个顶点结点
顶点结点
data | firstin | firstout |
弧结点
tailvex | headvex | hlink | tlink |
(弧尾) | (弧头) | (下一个弧头相同的弧) | (下一个弧尾相同的弧) |
邻接多重表(针对无向图、可以解决每条边存储两遍的问题)
特点:容易得出顶点和边的信息
不方便操作(删除一条边需要找到两个结点)
顶点结点
Data | firstedge |
顶点数据 | 指向第一条依附该顶点的边 |
边结点
mark | ivex | ilink | jvex | jlink | info |
标志域 标记此边是否被搜索过 | 该边依附的两个顶点在表头数组的位置 | 指向依附于ivex的下一条边 | 该边依附的两个顶点在表头数组的位置 | 指向依附于jvex的下一条边 |