目录
一、初识图
(1)图的定义
图(Graph)是由顶点的有穷非空集合(必须存在顶点)和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
(2)图的分类
若顶点到顶点之间的边设有方向,则称这条边为无向边。如果图中任意两个顶点之间的边都是无向边,则称该图为无向图。

若顶点到顶点的边有方向,则称这条边为有向边,也称为弧。如果图中任意两个顶点之间的边都是有向边,则称该图为有向图。
除此之外还有:
- 简单图
- 无向完全图
- 有向完全图
- 稠密图
- 带权图(网)
- ...
二、图的存储结构
(1)邻接矩阵
图的邻接矩阵存储方式是用二维数组来表示图,我们来看一个实例
它的邻接矩阵为:
简单解释一下,对于矩阵的主对角线的值,即arc[0][0]、arc[1][1]、arc[2[2]、arc[3][3],全为0是因为不存在顶点到自身的边,比如到
。arc[0][1]=1是因为
到
的边存在,而arc[1][3]=0是因为
到
的边不存在。并且由于是无向图,
到
的边不存在,意味着
到
的边也不存在。所以无向图的边数组是一个对称矩阵。
有了这个矩阵,我们就可以很容易地知道图中的信息。
- 我们要知道某个顶点的度,其实就是这个顶点在邻接矩阵中第ⅰ行(或第i列)的元素之和。比如顶点1的度就是1+0+1+0=2。
- 求顶点M的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点。
有向图的邻接矩阵也是一样
而如果是带权图的话,如果两个顶点可达,那么我们就将arc[i][j]的1变为他们两点之间的权值,如果两个顶点之间不可达,那么我们就将arc[i][j]的0变为无穷大。这里需要注意自己到自己的权值为0。
邻接矩阵(无向图)的实现:
#include<iostream>
using namespace std;
#define MAXVEX 100//最大顶点数
typedef char VertexType;//顶点类型
typedef int EdgeType;//边上的权值类型
typedef struct
{
VertexType vexs[MAXVEX];//顶点表
EdgeType arc[MAXVEX][MAXVEX];//邻接矩阵
int numVertexte;//当前顶点数
int numEdges;//当前边数
}MGraph;
void CreateMGraph(MGraph* G)//建立无向网图的邻接矩阵表示
{
int i, j, k, w;
cout << "请输入顶点数和边数:" << endl;
cin >> G->numVertexte >> G->numEdges;
for ( i = 0; i < G->numVertexte; ++i)//读入顶点信息,建立顶点表
{
cin >> G->vexs[i];
}
for (i = 0; i < G->numVertexte; ++i)
{
for ( j = 0; j < G->numVertexte; ++j)
{
G->arc[i][j] = INT_MAX;//邻接矩阵初始化
}
}
for (k = 0; k < G->numEdges; ++k)
{
cout << "输入边(vi,vj)上的下标i,下标 j 和权 w:" << endl;
cin >> i >> j >> w;
G->arc[i][j] = w;
G->arc[j][i] = G->arc[i][j];
}
}
int main()
{
MGraph G;
CreateMGraph(&G);
return 0;
}
(2)邻接表(无向图)
邻接矩阵是不错的一种图存储结构,但是我们也发现,对于边数相对顶点较少的图,这种结构是存在对存储空间的极大浪费的。于是引出了链式存储的结构。
邻接表的处理办法是这样。
- 图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过数组可以较容易地读取顶点信息,更加方便。另外,对于顶点数组中,每个数据元素还需要存储指向第一个邻接点的指针,以便于查找该顶点的边信息。
- 图中每个顶点的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用单链表存储。
例如
无向图的邻接表
有向图的邻接表
对于带权值的网图
邻接表(无向图)的实现
#include<iostream>
using namespace std;
#define MAXVEX 100//最大顶点数
typedef char VertexType;//顶点类型
typedef int EdgeType;//边上的权值类型
typedef struct EdgeNode
{
int adjvex;//邻接点域,存储该顶点对应的下标
EdgeType weight;//用于存储权值,对于非网图可以不需要
struct EdgeNode* next;//指向下一个邻接点
}EdgeNode;
typedef struct VertexNode //顶点表结点
{
VertexType data;//存储顶点信息
EdgeNode* firstedge;//指向该顶点的第一个相邻结点
}VertexNode,AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numVertexes;//当前顶点数
int numEdges;//当前边数
}GraphAdjList;
void CreateALGraph(GraphAdjList* G)//建立无向网图的邻接矩阵表示
{
int i, j, k;
EdgeNode* e;
cout << "请输入顶点数和边数:" << endl;
cin >> G->numVertexes >> G->numEdges;
for ( i = 0; i < G->numVertexes; ++i)//读入顶点信息,建立顶点表
{
cin >> G->adjList[i].data;
G->adjList[i].firstedge = nullptr;
}
for (k = 0; k < G->numEdges; ++k)//建立邻接表
{
cout << "输入边(vi,vj)上的顶点序号:" << endl;
cin >> i >> j;
e = new EdgeNode;
e->adjvex = j;
e->next = G->adjList[i].firstedge;
G->adjList[i].firstedge = e;//头插法
e = new EdgeNode;
e->adjvex = i;
e->next = G->adjList[j].firstedge;
G->adjList[j].firstedge = e;//头插法
}
}
int main()
{
GraphAdjList G;
CreateALGraph(&G);
return 0;
}
(3)其他
除上述两种方法之外,图的存储方式还有十字链表,邻接多重表,边集数组等。