10.2 图的存储
一般来说,图的存储方式有两种:邻接矩阵和邻接表。这两种存储方式各有优势,需要在不同的情况下选择使用。
10.2.1 邻接矩阵
设图G(V,E)的顶点标号为0,1 ,…… ,N-1 ,那么可以令二维数组G[N][N]的两维分别表示图的顶点标号,即如果G[i][j]为1,则说明顶点i和顶点j之间有边;如果G[i][j]为0,则说明顶点i和顶点j之间不存在边,而这个二维数组G[N][N]则被称为邻接矩阵。另外,如果存在边权,则可以令G[i][j]存放边权,对不存在的边可以设边权为0、-1或是一个很大的数。
下图举例一个无向图以及对应的邻接矩阵(边权为0表示不存在边),显然对于无向图来说,邻接矩阵是一个对称矩阵。
邻接矩阵只使用于顶点数目不太大(一般不超过1000)的题目。
10.2.2 邻接表
设图G(V,E)的顶点编号为0,1,…… ,N-1,每个顶点都可能有若干条出边,如果把同一个顶点的所有出边放在一个列表中,那么N个顶点就会有N个列表(没有出边,则对应空表)。这N个列表被称为图G的邻接表,记为Adj[N],其中Adj[i]存放顶点i的所有出边组成的列表,这样Adj[0],Adj[1],…… ,adj[N-1]就分别都是一个列表。由于列表可以用链表实现,可以得到对应的邻接表,其中Adj[0]用链表连接了两个结点,每个结点存放一条边的信息(括号外的数字是边的终点编号,括号内的数字是边权),于是0号顶点有两条出边:一条的终点为1号顶点(边权为2);另一条边的终点为4号顶点(边权为1)。而对Adj[4]来说,它表示4号顶点的三条出边的信息,这三条出边的终点分别是0号顶点、1号顶点、3号顶点,边权分别为1、2、1。
另外,链表也可以通过vector实现。
由于vector有变长数组之称,因此可以开一个vector数组Adj[N],其中N为顶点个数。
这样每个Adj[i]就都是一个变长数组vector,使得存储空间只与图的边数有关。
vector<int> Adj[N] ;
如果想添加一条从1号顶点到达3号顶点的有向边,只需要在Adj[1]中添加终点编号3即可。如果是无向边,就再添加一条从3号顶点到达1号顶点的边。
Adj[1].push_back(3) ;
如果需要同时存放边的终点编号和边权,那么可以建立结构体Node,用来存放每条边的结点编号和边权。
struct Node{
int v ; // 边的终点编号
int w ; //边权
};
这样,vector邻接表中的类型就是Node
vector<Node> Adj[N] ;
在一些顶点数目较大(一般顶点个数在1000以上)的情况下,一般都需要使用邻接表而非邻接矩阵。