链表、链式前向星

讲链表的时候就卡在这里了,最短路又卡在链式前向星上了,毕竟是图论基础,觉得还是有必要写一写防止下次再懵。

链表都是头插法!!即每次我们给他插一个头。

普通链表

先初始化令head=-1,idx=-1.

void add_tohead(int x) {		//向链表头插入一个元素
	e[idx] = x ;  //新建一个节点
	ne[idx] = head ;  	//新建节点的next指向原来的head
	head = idx ; 	//head指向我新建的节点
	idx ++ ;
}

链表、链式前向星_第1张图片

e数组存的是我们想要的数值或者其他的,ne数组存我们希望的顺序,若不改变则是上一个元素的下标,每次借用head指向最新的元素下标,再次加入借用head更新ne数组并再次更新head。

void remove(int x) {	//删除一个元素的后一个元素
	ne[x] = ne[ne[x]] ; 	//当前节点的next指向原来的next的next
}//ne数组里存的是上一个元素的下标

链表、链式前向星_第2张图片

 我们将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 ++ ;
}

链表、链式前向星_第3张图片

 新建一个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;
}

链表、链式前向星_第4张图片

主要理解head[u]存的到底是什么?

head[u]存了所有以u为一个端点的边。边是怎么存的呢?我们这里用结构体数组存了每一条边,所以准确地说, head[u]存了所有以u为端点的结构体下标。

再注意一点,我们的结构体存储的真的是准确的边吗?并不是,我们只存了他的一个端点和距离,而用本该是另一个端点的位置存储了该端点的上一条边的下标。

为什么?

因为我们建图是为了之后的遍历,我们的遍历方式是选点,遍历该点的所有边找最短路,那么我们在建图的时候就应该选择可以找出一个点的所有边的方式,这就是链式前向星。此时,每条边出现/输入的顺序对结果无影响(反正是要遍历)。

最后一点注意!!题目要求是无向图的时候,我们要建双向边,上述代码建了一个单向的边,即我们把该边存入了u端点而未存入v端点,所以最好把建图函数拎出来,主函数输入一次,建两次边。

你可能感兴趣的:(图论,链表,数据结构)