讲链表的时候就卡在这里了,最短路又卡在链式前向星上了,毕竟是图论基础,觉得还是有必要写一写防止下次再懵。
链表都是头插法!!即每次我们给他插一个头。
普通链表
先初始化令head=-1,idx=-1.
void add_tohead(int x) { //向链表头插入一个元素
e[idx] = x ; //新建一个节点
ne[idx] = head ; //新建节点的next指向原来的head
head = idx ; //head指向我新建的节点
idx ++ ;
}
e数组存的是我们想要的数值或者其他的,ne数组存我们希望的顺序,若不改变则是上一个元素的下标,每次借用head指向最新的元素下标,再次加入借用head更新ne数组并再次更新head。
void remove(int x) { //删除一个元素的后一个元素
ne[x] = ne[ne[x]] ; //当前节点的next指向原来的next的next
}//ne数组里存的是上一个元素的下标
我们将ne数组中下标为x的元素更新为下标为该元素的元素,看图解,这样我们在访问时,访问到e数组下标为x的元素后,i=ne[x],即为y,相当于我们没有删除e数组的元素而是跳过了他,使得遍历无法访问到该值。
void add(int k, int x) { //在第k个插入的数后面添加一个元素
e[idx] = x ; //新建一个节点
ne[idx] = ne[k] ; //新建节点的next指向原来next[k]
ne[k] = idx ; //原来的next[k]指向新建的节点 26行和27行不能调换!
idx ++ ;
}
新建一个e数组元素,同样地,不改变e数组而是改变ne数组(改变访问顺序),看图解,我们访问到ne[k]时,其实是访问了e[idx]即新建的元素,之后i更新为ne[idx],此时访问的是e[t],然后继续回归顺序。这样就达到了在k之后新插一个元素的要求。
for (int i = head ; i != -1 ; i = ne[i]) {
cout << e[i] << " \n"[ne[i] == -1] ;
}
遍历的实现。当ne[i] == -1的时候就说明已经到达链表尾部(或者也可以不初始化,中间句直接i就可以),即第一个插入的元素已进行循环,该链表已遍历。
链式前向星
最短路问题 dijkstra算法建图
struct node {
int to, nex, val;
} arr[N];
int tot = 0;
int dis[N];
bool vis[N];
void add(int u, int v, int c) { //表示u-v间有一条长度为c的路
arr[++tot].to = v;
arr[tot].val = c;
arr[tot].nex = head[u];
head[u] = tot;
}
主要理解head[u]存的到底是什么?
head[u]存了所有以u为一个端点的边。边是怎么存的呢?我们这里用结构体数组存了每一条边,所以准确地说, head[u]存了所有以u为端点的结构体下标。
再注意一点,我们的结构体存储的真的是准确的边吗?并不是,我们只存了他的一个端点和距离,而用本该是另一个端点的位置存储了该端点的上一条边的下标。
为什么?
因为我们建图是为了之后的遍历,我们的遍历方式是选点,遍历该点的所有边找最短路,那么我们在建图的时候就应该选择可以找出一个点的所有边的方式,这就是链式前向星。此时,每条边出现/输入的顺序对结果无影响(反正是要遍历)。
最后一点注意!!题目要求是无向图的时候,我们要建双向边,上述代码建了一个单向的边,即我们把该边存入了u端点而未存入v端点,所以最好把建图函数拎出来,主函数输入一次,建两次边。