首先我们依照读入的顺序为每一条边进行编号(1~m)。
比方第一条边“1 4 9”的编号就是1,“1
3 7”这条边的编号是5。
这里用u、v和w三个数组用来记录每条边的详细信息,即u[i]、v[i]和w[i]表示第i条边是从第u[i]号顶点到v[i]号顶点(u[i]àv[i]),且权值为w[i]。
再用一个first数组来存储每一个顶点当中一条边的编号。以便待会我们来枚举每个顶点全部的边(你可能会问:存储当中一条边的编号就能够了?不可能吧,每一个顶点都须要存储其全部边的编号才行吧!甭着急。继续往下看)。比方1号顶点有一条边是
“1 4 9”(该条边的编号是1),那么就将first[1]的值设为1。假设某个顶点i没有以该顶点为起始点的边,则将first[i]的值设为-1。如今我们来看看详细怎样操作,初始状态例如以下。
咦?上图中怎么多了一个next数组。有什么作用呢?不着急。待会再解释。如今先读入第一条边“1
4 9”。
读入第1条边(1
4 9),将这条边的信息存储到u[1]、v[1]和w[1]中。
同一时候为这条边赋予一个编号,由于这条边是最先读入的,存储在u、v和w数组下标为1的单元格中,因此编号就是1。这条边的起始点是1号顶点。因此将first[1]的值设为1。
另外这条“编号为1的边”是以1号顶点(即u[1])为起始点的第一条边,所以要将next[1]的值设为-1。也就是说,假设当前这条“编号为i的边”,是我们发现的以u[i]为起始点的第一条边。就将next[i]的值设为-1(貌似的这个next数组非常神奇啊⊙_⊙)。
读入第2条边(4
3 8),将这条边的信息存储到u[2]、v[2]和w[2]中。这条边的编号为2。
这条边的起始顶点是4号顶点,因此将first[4]的值设为2。另外这条“编号为2的边”是我们发现以4号顶点为起始点的第一条边,所以将next[2]的值设为-1。
读入第3条边(1
2 5)。将这条边的信息存储到u[3]、v[3]和w[3]中。这条边的编号为3,起始顶点是1号顶点。我们发现1号顶点已经有一条“编号为1 的边”了。假设此时将first[1]的值设为3,那“编号为1的边”岂不是就丢失了?我有办法,此时仅仅需将next[3]的值设为1就可以。如今你知道next数组是用来做什么的吧。
next[i]存储的是“编号为i的边”的“前一条边”的编号。
读入第4条边(2 4 6),将这条边的信息存储到u[4]、v[4]和w[4]中,这条边的编号为4,起始顶点是2号顶点,因此将first[2]的值设为4。
另外这条“编号为4的边”是我们发现以2号顶点为起始点的第一条边。所以将next[4]的值设为-1。
读入第5条边(1 3 7),将这条边的信息存储到u[5]、v[5]和w[5]中,这条边的编号为5,起始顶点又是1号顶点。此时须要将first[1]的值设为5。并将next[5]的值改为3。
此时,假设我们想遍历1号顶点的每一条边就非常easy了。1号顶点的当中一条边的编号存储在first[1]中。其余的边则能够通过next数组寻找到。请看下图。
算法8:巧妙的邻接表(数组实现)
遍历1号顶点全部边的代码例如以下。
1
2
3
4
5
6
|
k=first[1];
while (k!=-1)
{
printf ( "%d
%d %d
" ,u[k],v[k],w[k]);
k=next[k];
}
|