对于稀疏图来说,M要远远小于N2。
先上数据。例如以下。
1
2
3
4
5
6
|
4
5 1
4 9 4
3 8 1
2 5 2
4 6 1
3 7 |
n表示顶点个数(顶点编号为1~n),m表示边的条数。接下来m行表示,每行有3个数x y z。表示顶点x到顶点y的边的权值为z。下图就是一种使用链表来实现邻接表的方法。
这里我将为大家介绍另一种使用数组来实现的邻接表,这是一种在实际应用中很easy实现的方法。这样的方法为每一个顶点i(i从1~n)也都保存了一个类似“链表”的东西。里面保存的是从顶点i出发的全部的边,详细例如以下。
比方第一条边“1 4 9”的编号就是1,“1 3 7”这条边的编号是5。
同一时候为这条边赋予一个编号,由于这条边是最先读入的,存储在u、v和w数组下标为1的单元格中,因此编号就是1。这条边的起始点是1号顶点。因此将first[1]的值设为1。
这条边的起始顶点是4号顶点,因此将first[4]的值设为2。另外这条“编号为2的边”是我们发现以4号顶点为起始点的第一条边,所以将next[2]的值设为-1。
next[i]存储的是“编号为i的边”的“前一条边”的编号。
另外这条“编号为4的边”是我们发现以2号顶点为起始点的第一条边。所以将next[4]的值设为-1。
由于在为每一个顶点插入边的时候都直接插入“链表”的首部而不是尾部。只是这并不会产生不论什么问题。这正是这样的方法的其妙之处。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
int n,m,i; //u、v和w的数组大小要依据实际情况来设置,要比m的最大值要大1 int u[6],v[6],w[6]; //first和next的数组大小要依据实际情况来设置,要比n的最大值要大1 int first[5],next[5]; scanf ( "%d
%d" ,&n,&m); //初始化first数组下标1~n的值为-1,表示1~n顶点临时都没有边 for (i=1;i<=n;i++) first[i]=-1; for (i=1;i<=m;i++) { scanf ( "%d
%d %d" ,&u[i],&v[i],&w[i]); //读入每一条边 //以下两句是关键啦 next[i]=first[u[i]]; first[u[i]]=i; } |
请看下图:
1
2
3
4
5
6
|
k=first[1]; //
1号顶点当中的一条边的编号(事实上也是最后读入的边) while (k!=-1) //其余的边都能够在next数组中依次找到 { printf ( "%d
%d %d
" ,u[k],v[k],w[k]); k=next[k]; } |
1
2
3
4
5
6
7
8
9
|
for (i=1;i<=n;i++) { k=first[i]; while (k!=-1) { printf ( "%d
%d %d
" ,u[k],v[k],w[k]); k=next[k]; } } |
因此稀疏图选用邻接表来存储要比邻接矩阵来存储要好非常多。