数组实现邻接表
思路:
临接表是储存一条边的起点,终点,权重,用数组实现时用srtuct数组e中储存上述内容。
在储存时,我们储存起点的编号和终点的编号。
为了方便的遍历各点,我们还需要储存下一条边的位置。
emmmm非常抽象,我都不知道自己解释的啥玩意。
我们来用图片、代码和实例来说话(原谅我的画风)
int head[7], total, n, m; struct edge { int u, v, w; }e[10];
左边的条条是数组head,储存的是每个点第一个出边在数组e中的位置
右边的框框是数组e,每个格子储存3个元素,u起点,v终点,w权重
我们假设一共有6个点,9条边。
那我们一共就要储存6个点的第一条出边在e中的位置,即head[6+1]注意数组的结尾。
9条边分别储存在数组e中,即e[9+1]结尾同理。
解释完了变量我们开始输入
数据:
6 9 //点数和边数 1 2 1 //u v w w权重这里不多说,最后加上就行 1 3 12 2 3 9 2 4 3 3 5 5 4 3 4 4 5 13 4 6 15 5 6 4
1 2 1
点1连向点2权重为1
我们将head[1]赋值为1,表示点1的第一条出边储存在e[1]中。
那我们第一条边的起点有了,终点2呢?
我们将e[i]的元素u赋值为2,表示这条边的中点为2。
w为权重不解释。v马上解释,我们先讨论数组head。
这里的head可以看作是指针,指向点i的最后一条出边。
为什么是最后一条呢?刚才储存的明明是第一条。
先保留疑问,我们先储存第二条边。
1 3 12
点1连向点3 权重为12
根据刚才的操作,我们应该把head[1]赋值为2,表示1到3这条边储存在数组e[2]这个位置。
可是head[1]已经被占用了。
这时我们当然不能直接覆盖,我们可以先将head[1]的值保存在e[2]的v里。
那这个怎么理解?
刚才解释了head[1]=1表示点1的一条出边在e[1]中。
那我们将head[1]的值转移到e[2]的v中表示1到3这条边的下一条边在e[1]中储存。
可我们为什么要这么储存边?
因为我们在储存图时只关心点与点之间的联通关系,并不关心储存的顺序。
比如 1到2 1到3 和 1到3 1到2没有任何区别,只是顺序不同罢了。
将head中的数转移走了我们就可以储存新的起点了。
这样我们就储存了1>>>2 和1>>>3这两条边了。
指针的指向如下
因为1>>>2这条边是1的第一条出边,所以没有下一条出边,那v储存的便是0,表示没有下一条了,我这里是随便指向了end就表示没了。
然后让我们来储存剩下的点。
2 3 9
我们将这条边储存在e[3]中
head[2]=3;点2的第一条出边在e[3]中
然后将终点和下一条边的位置存在e[3]中
2 4 3
下面给出整张图的
代码实现
#include <iostream> #include <cstdio> #define N 1001 int head[N], total, n, m; struct edge { int u, v, w; }e[N]; void add(int u, int v) { total++; e[total].u = v; e[total].v = head[u]; head[u] = total; return; } void test() { for (int i = 1; i <= m; i++) { printf("%d >>> ", i); for (int j = head[i]; j != 0; j = e[j].v) printf("%3d", e[j].u); printf("\n"); } return; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { int a, b; scanf("%d%d", &a, &b); add(a, b); } test(); return 0; }