数组模拟链表

链表的操作,一般在c语言中都是用指针和结构体来完成的,这样的链表我们也叫做动态链表。
但是动态的链表有一个比较致命的缺点,就是它创造一个新的连接的时候,速度会特别特别的慢。如果在算法题中使用动态链表,TLE基本上就是板上钉钉了。
所以学习一下利用数组来模拟链表还是蛮重要的。用数组模拟的链表也叫静态链表,它的缺点是会有些浪费内存,但优点是你可以快到飞起QwQ
其实只要理解了链表,用数组来完成模拟的过程并不算复杂。。。所以理解链表以后记记模板就差不多欧克了QwQ

1.单链表

<1>一般的单链表

在用数组模拟链表时,我们可以用e[i]来表示当前节点的权值,ne[I]来表示当前节点指向的下一节点(也就是动态链表中的next),head表示头节点,idx来记录下标。
然后一般会用到的操作就是在表头加入元素,在表中任意一点加入元素,以及删除任意一个节点这三个操作。其实也不多,用代码实现起来也比较的简单。

const int N = 100010;
int e[N], ne[N], head, idx;

void init()   //初始化链表
{
	head = -1;
	idx = 0;
}

void add_to_head(int x)   //加入元素到表头
{
	e[idx] = x;   //先记录输入节点的权值
	ne[idx] = head;     //让输入节点的指针指向head指向的节点
	head = idx ++;    //让head节点指向该节点,同时记录的下标加一
}

void add(int k, int x)   //加入元素到任意节点后
{
	e[idx] = x;
	ne[idx] = ne[k];     //这里的理解和上面差不多,把head换成了ne[k]而已
	ne[k] = idx ++;
}

void remove(int k)   //删除,注意我们删除的是第k + 1个节点
{
	if(k == 0) head = ne[head];
	else ne[k] = ne[ne[k]];
}

//这里还要有一点就是链表的遍历,输出或查询链表的时候会用到
void ptinrt()
{
	for(int i = head; i != -1; i = ne[i])
		printf("%d ", e[i]);
	printf("\n");
}

<2>邻接链表

邻接链表其实也就是好多个单链表,操作和上面的一样,把头结点head改成一个数组h[i]就可以了。
同样的,初始化是要把所有的h[i]赋值为-1
邻接链表的主要作用是在图论题中可以比较方便的储存边,比如最短路的堆优化版Dijkstra算法和SPFA算法中,都有用到邻接链表来储存边界。

2.双链表

双链表是指向两个方向的,由于不好操作,所以我们直接让head = 0, tail = 1来偷懒定出表头和表尾。
然后因为有两个指向,我们就不在用ne[i]记录指向了,我们用r[i]来确定右指向,l[i]来确定左指向,然后e[i]和idx,是和单链表一样的。

const int N = 100010;
int e[N], r[N], l[N], idx;

void init()
{
	l[1] = 0;
	r[0] = 1;
	idx = 2;  //因为0,1都被占用了,所以直接从2开始建立
}

void add(int k, int x)  // 在k右方添加一个节点,如果要在k左方带入k时用l[k]即可
{
	e[x] = x;
	r[idx] = r[k];
	l[idx] = k;
	l[r[k]] = idx;
	r[k] = idx ++; 
}

void remove(int k)  // 删除
{
	r[l[k]] = r[k];  // 我左边的右边指向我的右边
	l[r[k]] = l[k];  // 我右边的左边指向我的左边
}

void print()  // 遍历
{
	for(int i = r[0], i != 1, i = r[i])   // 往一个方向遍历就可以了
		printf("%d ", e[i]);
	printf("\n");
}

好了,静态链表就是介个样子了,操作起来还是蛮简单的。
我要溜去玩耍了QwQ

你可能感兴趣的:(数组模拟链表)