1.邻接矩阵
邻接矩阵就是使用一个二维矩阵表示图中顶点之间的边。
若是有权图,arcs[i][j] (0≤i, j<Nv; Nv为图中的顶点数)表示顶点i和j之间的边上权值,若i和j之间没有边,则将arcs[i][j]设置为INIFINITY(一个取不到的大值)。对于无向图,邻接矩阵满足:arcs[i][j] == arcs[j][i],它是一个方阵,而有向图的邻接矩阵不满足此特性,arcs[i][j]表示弧<Vi, Vj>的权,Vi为弧尾,Vj为弧头,即由Vi指向Vj。
若是无权图,可以把它当作边上权值是1的有权图,即若顶点i和j之间有边,则arcs[i][j] = 1,若顶点i和j之间没有边,则arcs[i][j] = 0。
#include <iostream>
using namespace std;
typedef char VertexType; // 顶点类型,自定义成char
typedef int WeightType; // 边上的权值类型,自定义成int
#define MAXVEX (100) // 最大顶点数
#define INFINITY (65535) // 用65535表示∞,表示顶点之间没有边
typedef struct {
VertexType vexs[MAXVEX]; // 顶点数组
WeightType arcs[MAXVEX][MAXVEX]; // 邻接矩阵,二维矩阵,记录顶点间的连接情况,即边表
int Nv, Ne; // 图中顶点数和边数
} MGraph;
/* 建立图的邻接矩阵结构 */
void createMGraph(MGraph* G) {
cout << "输入顶点数和边数:" << endl;
cin >> G->Nv >> G->Ne;
// 将邻接矩阵所有元素初始化为:INFINITY
for (int i = 0; i < G->Nv; i++)
{
for (int j = 0; j < G->Nv; j++)
{
if (i == j)
{
G->arcs[i][j] = 0; // 主对角线初始化为0
}
else
{
G->arcs[i][j] = INFINITY;
}
}
}
cout << "输入顶点信息:" << endl;
for (int i = 0; i < G->Nv; i++)
{
cin >> G->vexs[i];
}
// 读入Ne条边,建立邻接矩阵
int i, j;
WeightType w;
for (int k = 0; k < G->Ne; k++)
{
cout << "输入边(vi, vj)的下标i、j及权值w:" << endl;
cin >> i >> j >> w;
G->arcs[i][j] = w;
// G->arcs[j][i] = G->arcs[i][j]; // 无向图的邻接矩阵是对称阵,若G是有向图则不需要这行
}
}
int main() {
MGraph* G = new MGraph;
createMGraph(G);
// 打印顶点数组
cout << "\n顶点数组" << endl;
for (int i = 0; i < G->Nv; i++)
{
cout << G->vexs[i] << "\t";
}
// 打印邻接矩阵的值
cout << "\n邻接矩阵" << endl;
for (int i = 0; i < G->Nv; i++)
{
for (int j = 0; j < G->Nv; j++)
{
cout << G->arcs[i][j] << "\t";
}
cout << endl;
}
delete G;
}
运行结果:
以《大话数据结构》图7-4-4的有向图为例。
2.邻接表
邻接表使用链表来表示图中顶点之间的边。
将顶点Vi的所有邻接点链接成一个链表,然后在顶点数组的每个结点中设计一个指针,指向该链表,整体结构类似于哈希表。
顶点:按编号顺序将顶点数据存储在一维数组中
关联同一顶点的边(以顶点为尾的弧):用线性链表存储
#include <iostream>
using namespace std;
typedef char VertexType; // 顶点类型,自定义成char
typedef int WeightType; // 边上的权值类型,自定义成int
#define MAXVEX (100) // 最大顶点数
/* 边表结点 */
typedef struct EdgeNode {
int adjvex; // 邻接点域,存储该顶点对应的下标
WeightType weight; // 权值域,存储顶点权值,非网图(无权图)不必定义
EdgeNode *next; // 指向下一个邻接点
} EdgeNode;
/* 顶点结点 */
typedef struct VertexNode {
VertexType data; // 顶点域,存储顶点值
EdgeNode *firstEdge; // 边表头指针,指向第一个邻接点
} VertexNode, AdjList[MAXVEX]; // AdjList表示邻接表类型
typedef struct {
AdjList adjList; // 邻接表,相当于:VertexNode adjList[MAXVEX];
int Nv, Ne; // 图中顶点数和边数
} ALGraph;
/* 建立图的邻接表结构 */
void createALGraph(ALGraph* G) {
cout << "输入顶点数和边数:" << endl;
cin >> G->Nv >> G->Ne;
// 读入顶点信息
cout << "输入顶点信息:" << endl;
for (int i = 0; i < G->Nv; i++)
{
cin >> G->adjList[i].data; // 读入顶点数值
G->adjList[i].firstEdge = nullptr; // 将边表指针置为空
}
// 读入Ne条边,建立邻接表
int i, j;
WeightType w;
EdgeNode* e;
for (int k = 0; k < G->Ne; k++)
{
cout << "输入边(vi, vj)的下标及i、j及权值w:" << endl;
cin >> i >> j >> w;
e = new EdgeNode;
e->adjvex = j; // 邻接点下标为j
e->weight = w; // 边(vi, vj)上的权值
e->next = G->adjList[i].firstEdge; // 将e的指针指向当前顶点指向的结点
G->adjList[i].firstEdge = e; // 将当前顶点的指针指向e
/* 52、53这两行:使用头插法将结点插入链表中 */
// 以下为无向图需要部分,有向图不需要
/*e = new EdgeNode;
e->adjvex = i;
e->weight = w;
e->next = G->adjList[j].firstEdge;
G->adjList[j].firstEdge = e;*/
}
}
/* 销毁邻接表 */
void destroyAdjList(ALGraph* G) {
for (int i = 0; i < G->Nv; i++)
{
while (G->adjList[i].firstEdge)
{
EdgeNode* e = G->adjList[i].firstEdge;
G->adjList[i].firstEdge = e->next;
delete e;
}
}
}
ostream& operator<<(ostream& os, VertexNode vex) {
os << vex.data;
return os;
}
int main() {
ALGraph* G = new ALGraph;
// 建图
createALGraph(G);
// 打印顶点数组
cout << "\n顶点数组" << endl;
for (int i = 0; i < G->Nv; i++)
{
cout << G->adjList[i].data << "\t";
}
// 打印邻接表的值
cout << "\n邻接表" << endl;
for (int i = 0; i < G->Nv; i++)
{
cout << "顶点" << G->adjList[i].data << "的邻接点有:";
EdgeNode* p = G->adjList[i].firstEdge;
while (p)
{
cout << G->adjList[p->adjvex] << "(" << p->weight << ")" << "\t";
p = p->next;
}
cout << endl;
}
// 销毁邻接表
destroyAdjList(G);
delete G;
}
运行结果:
以《大话数据结构》图7-4-8的有向图为例。