图的存储结构
1 邻接矩阵
两个数组来表示图
1)一个【一维数组】存储图的【顶点】信息
2)一个【二维数组】存储图的【边】的信息
a) 无向图
无向图的邻接矩阵是一个 对称矩阵 并且对角线上元素数值为 0
因为无向图只要两个顶点v1,v2之间有边,那么不管是v1 -> v2 还是 v2 -> v1都是有边
又因为,在图结构中,目前只研究简单图结构(无向图两个顶点之间只有一条边
并且顶点没有到自身的边)所以邻接矩阵对角线上的元素为 0
两个顶点v1,v2之间有边 -> 邻接矩阵中的(v1,v2) = 1; 否则 (v1,v2)= 0
无向图中的度:
顶点所对应的行 或 列 中非0元素的个数,就是该顶点的度
b) 有向图
有向图的邻接矩阵不是一个对称矩阵,但是矩阵主对角线上的值 仍然为 0,原因和无向图中的一样
两个顶点的边是由顺序的 (v1,v2)有边,!= (v2,v1)就有边,正因为这个原因
有向图的邻接矩阵不是对称矩阵
邻接矩阵中的值,比如(v1,v2)的值,就是v1,v2之间的权值
有向图中的度分为,出度,入度,在邻接矩阵中,入度,出度是这样计算的:
入度:顶点所对应的【列】中非0元素的个数
出度:顶点所对应的【行】中非0元素的个数
3)创建邻接矩阵
a) 创建邻接矩阵存储结构
typedef char VertexType;
typedef int EdgeType;
#define Max 100 //最大顶点数
#define INFINITY 65535 //如果两个顶点之间没有权重,就为 无穷大
typedef struct
{
VertexType vexs[Max]; //存储顶点信息的一维数组
EdgeType arc[Max][Max]; //二维数组组成的邻接矩阵,可看作边表
int numEdge,numVertex; //边数 顶点数
}MGraph;
b) 构造图,给顶点表和邻接矩阵输入数据的过程
void CreatMGraph(MGraph *G)
{
//01 输入边数,顶点数
int i,j;
int numEdge,numVertex;
cout << "输入边数,顶点数: " ;
cin >> numEdge >> numVertex;
//02 输入顶点信息,建立顶点表
cout << "请输入顶点信息: " << endl;
for(i = 0; i < numVertex; i++)
{
cin >> G->vexs[i];
}
//03 初始化邻接矩阵
for(i = 0; i < numEdge; i++)
{
for(j = 0; j < numEdge; j++)
{
G->arc[i][j] = INFINITY;
}
}
//04 输入边信息
for(int k = 0; k < numVertex; k++)
{
cout << "请输入边(v1,v2)两个顶点的下标i,j,以及边权重w" <<endl;
cin >> i >> j >> w;
G->arc[i][j] = w; //边的权重为w
G->arc[j][i] = G->[i][j]; //无向图,矩阵对称
}
}
4)邻接矩阵的缺点:
当图中的表边数较少时,邻接矩阵仍然需要开辟 numVertex*numVertex个空间,这是对内存空间的浪费
2 邻接表
用一个一维数组或单链表记录图中顶点的信息
(data firstedge)data 顶点信息 firstedge第一个邻接点的指针
用一个线性链表存储每个顶点的所有邻接点
(agjvex weight next)agjvex 邻接点在一维数组(顶点表)中的下标 weight 权重 next 下一个结点的指针
邻接表由三部分结构体组成:
typedef char VertexType
typedef int EdgeType
1)记录邻接边信息的结构体 EdgeNode
typedef struct EdgeNode
{
int agjvex; //邻接点域,存储该点对应的下标
EdgeType weight; //存储权重,非网图可以不要
EdgeNode *next; //链域,指向下一个链接点
}EdgeNode;
2)记录顶点信息的结构体 VertexNode
typedef struct VertexNode
{
int data; //存储顶点数据
EdgeNode *firstedge;//边表头指针,指向与该点组成边的另一个点对应的下标
}VertexNode,AdjList[Max]; //AdjList[Max] 存储顶点信息的一维数组
多个VertexNode类型的元素组成一维数组,存储顶点信息
3)记录当前图中的顶点个数,边的个数 GraphAdjList
typedef struct GraphAdjList
{
AdjList adjList;
int numEdge,numVertex; //当前图 的顶点个数,边数
}GraphAdjList;
4)存储邻接边信息 (邻接边是 EdgeNode 类型的) GraphAdjList *G
a) 输入顶点个数numVertex,边的个数numEdge
循环输入顶点信息,建立顶点表,顶点信息存储在AdjList[i].data中
将G->adjList[i].firstedge = null
b) 建立边表
每循环一次,就向内存申请一次的空间e EdgeNode类型,存放邻接边信息,没有某种结构类型来存储,比如数组
输入(Vi,Vj)邻接边 的 序号i,j
e->adjvex = j; //顶点i的邻接序号
e->next = G->adjList[i].firstedge; //使用的是单链表中的头插法 将当前顶点的 firstedge 给 e指针指向
G->adjList[i].firstedge = e; //将当前顶点的 firstedge 指向 e
同样,对于下标为j的顶点,它的边表的建立如下:
e->adjvex = i;
e->next = G->adjList[j].firstedge;
G->adjList[j].firstedge = e;
3 十字链表
1)将邻接表和逆邻接表结合起来
2)顶点表结构:
data firstin firstout
data :顶点数据
firstin :顶点的入边的第一个结点
firstout :表示出边表头指针,指向该顶点的出边表中的第一个结点
3)边表结构:
tailvex headvex headlink tailink
tailvex : 弧【起】点在顶点表中的下标
headvex : 弧【终】点在顶点表中的下标
headlink :指入边表指针域,指向【终点相同】的下一条边
tailink : 指出边表指针域,指向【起点相同】的下一条边
4)代码和邻接表的类似:
typedef char VertexType;
typedef int EdgeType;
#define Max 65535
//01 顶点表结构体 VertexNode
typedef struct VertexNode
{
VertexType data;
EdgeNode *firstin;
EdgeNode * firstout;
}VertexNode,AdjList[Max];
//02 边表结构体 EdgeNode
typedef struct EdgeNode
{
int tailvex,headvex;
struct EdgeNode *headlink;
struct EdgeNode *tailink;
}EdgeNode;
//03 顶点数,边数
typedef struct GraphAdjList
{
AdjList adjList;
int numEdge,numVertex;
}GraphAdjList;
//04 建立十字链接表结构
void CreatOrthogonal(GraphAdjList *G)
{
int i,j;
//1 输入顶点个数numVertex,边的个数numEdge
cout << "输入边数,顶点数: ";
cin >> numEdge >> numVertex;
cout << endl;
//2 输入顶点信息,建立顶点表
cout << "输入顶点信息,建立顶点表:" << endl;
for(i = 0; i < numVertex; i++)
{
cin >> G->adjList[i].data;
G->adjList[i].firstin = null; //出入边表为空
G->adjList[i].firstout = null;
}
//3 构建十字链表结构
?
}
4 邻接多重表
ivex illink jvex jlink
illink : 指向依附顶点ivex的下一条边
jlink : 指向依附顶点jvex的下一条边
ivex jvex :与某条边依附的两个顶点在顶点表中的下标
5 边集数组
1 邻接矩阵
两个数组来表示图
1)一个【一维数组】存储图的【顶点】信息
2)一个【二维数组】存储图的【边】的信息
a) 无向图
无向图的邻接矩阵是一个 对称矩阵 并且对角线上元素数值为 0
因为无向图只要两个顶点v1,v2之间有边,那么不管是v1 -> v2 还是 v2 -> v1都是有边
又因为,在图结构中,目前只研究简单图结构(无向图两个顶点之间只有一条边
并且顶点没有到自身的边)所以邻接矩阵对角线上的元素为 0
两个顶点v1,v2之间有边 -> 邻接矩阵中的(v1,v2) = 1; 否则 (v1,v2)= 0
无向图中的度:
顶点所对应的行 或 列 中非0元素的个数,就是该顶点的度
b) 有向图
有向图的邻接矩阵不是一个对称矩阵,但是矩阵主对角线上的值 仍然为 0,原因和无向图中的一样
两个顶点的边是由顺序的 (v1,v2)有边,!= (v2,v1)就有边,正因为这个原因
有向图的邻接矩阵不是对称矩阵
邻接矩阵中的值,比如(v1,v2)的值,就是v1,v2之间的权值
有向图中的度分为,出度,入度,在邻接矩阵中,入度,出度是这样计算的:
入度:顶点所对应的【列】中非0元素的个数
出度:顶点所对应的【行】中非0元素的个数
3)创建邻接矩阵
a) 创建邻接矩阵存储结构
typedef char VertexType;
typedef int EdgeType;
#define Max 100 //最大顶点数
#define INFINITY 65535 //如果两个顶点之间没有权重,就为 无穷大
typedef struct
{
VertexType vexs[Max]; //存储顶点信息的一维数组
EdgeType arc[Max][Max]; //二维数组组成的邻接矩阵,可看作边表
int numEdge,numVertex; //边数 顶点数
}MGraph;
b) 构造图,给顶点表和邻接矩阵输入数据的过程
void CreatMGraph(MGraph *G)
{
//01 输入边数,顶点数
int i,j;
int numEdge,numVertex;
cout << "输入边数,顶点数: " ;
cin >> numEdge >> numVertex;
//02 输入顶点信息,建立顶点表
cout << "请输入顶点信息: " << endl;
for(i = 0; i < numVertex; i++)
{
cin >> G->vexs[i];
}
//03 初始化邻接矩阵
for(i = 0; i < numEdge; i++)
{
for(j = 0; j < numEdge; j++)
{
G->arc[i][j] = INFINITY;
}
}
//04 输入边信息
for(int k = 0; k < numVertex; k++)
{
cout << "请输入边(v1,v2)两个顶点的下标i,j,以及边权重w" <<endl;
cin >> i >> j >> w;
G->arc[i][j] = w; //边的权重为w
G->arc[j][i] = G->[i][j]; //无向图,矩阵对称
}
}
4)邻接矩阵的缺点:
当图中的表边数较少时,邻接矩阵仍然需要开辟 numVertex*numVertex个空间,这是对内存空间的浪费
2 邻接表
用一个一维数组或单链表记录图中顶点的信息
(data firstedge)data 顶点信息 firstedge第一个邻接点的指针
用一个线性链表存储每个顶点的所有邻接点
(agjvex weight next)agjvex 邻接点在一维数组(顶点表)中的下标 weight 权重 next 下一个结点的指针
邻接表由三部分结构体组成:
typedef char VertexType
typedef int EdgeType
1)记录邻接边信息的结构体 EdgeNode
typedef struct EdgeNode
{
int agjvex; //邻接点域,存储该点对应的下标
EdgeType weight; //存储权重,非网图可以不要
EdgeNode *next; //链域,指向下一个链接点
}EdgeNode;
2)记录顶点信息的结构体 VertexNode
typedef struct VertexNode
{
int data; //存储顶点数据
EdgeNode *firstedge;//边表头指针,指向与该点组成边的另一个点对应的下标
}VertexNode,AdjList[Max]; //AdjList[Max] 存储顶点信息的一维数组
多个VertexNode类型的元素组成一维数组,存储顶点信息
3)记录当前图中的顶点个数,边的个数 GraphAdjList
typedef struct GraphAdjList
{
AdjList adjList;
int numEdge,numVertex; //当前图 的顶点个数,边数
}GraphAdjList;
4)存储邻接边信息 (邻接边是 EdgeNode 类型的) GraphAdjList *G
a) 输入顶点个数numVertex,边的个数numEdge
循环输入顶点信息,建立顶点表,顶点信息存储在AdjList[i].data中
将G->adjList[i].firstedge = null
b) 建立边表
每循环一次,就向内存申请一次的空间e EdgeNode类型,存放邻接边信息,没有某种结构类型来存储,比如数组
输入(Vi,Vj)邻接边 的 序号i,j
e->adjvex = j; //顶点i的邻接序号
e->next = G->adjList[i].firstedge; //使用的是单链表中的头插法 将当前顶点的 firstedge 给 e指针指向
G->adjList[i].firstedge = e; //将当前顶点的 firstedge 指向 e
同样,对于下标为j的顶点,它的边表的建立如下:
e->adjvex = i;
e->next = G->adjList[j].firstedge;
G->adjList[j].firstedge = e;
3 十字链表
1)将邻接表和逆邻接表结合起来
2)顶点表结构:
data firstin firstout
data :顶点数据
firstin :顶点的入边的第一个结点
firstout :表示出边表头指针,指向该顶点的出边表中的第一个结点
3)边表结构:
tailvex headvex headlink tailink
tailvex : 弧【起】点在顶点表中的下标
headvex : 弧【终】点在顶点表中的下标
headlink :指入边表指针域,指向【终点相同】的下一条边
tailink : 指出边表指针域,指向【起点相同】的下一条边
4)代码和邻接表的类似:
typedef char VertexType;
typedef int EdgeType;
#define Max 65535
//01 顶点表结构体 VertexNode
typedef struct VertexNode
{
VertexType data;
EdgeNode *firstin;
EdgeNode * firstout;
}VertexNode,AdjList[Max];
//02 边表结构体 EdgeNode
typedef struct EdgeNode
{
int tailvex,headvex;
struct EdgeNode *headlink;
struct EdgeNode *tailink;
}EdgeNode;
//03 顶点数,边数
typedef struct GraphAdjList
{
AdjList adjList;
int numEdge,numVertex;
}GraphAdjList;
//04 建立十字链接表结构
void CreatOrthogonal(GraphAdjList *G)
{
int i,j;
//1 输入顶点个数numVertex,边的个数numEdge
cout << "输入边数,顶点数: ";
cin >> numEdge >> numVertex;
cout << endl;
//2 输入顶点信息,建立顶点表
cout << "输入顶点信息,建立顶点表:" << endl;
for(i = 0; i < numVertex; i++)
{
cin >> G->adjList[i].data;
G->adjList[i].firstin = null; //出入边表为空
G->adjList[i].firstout = null;
}
//3 构建十字链表结构
?
}
4 邻接多重表
ivex illink jvex jlink
illink : 指向依附顶点ivex的下一条边
jlink : 指向依附顶点jvex的下一条边
ivex jvex :与某条边依附的两个顶点在顶点表中的下标
5 边集数组
begin end weight
部分代码摘抄自《大话数据结构》