「数据结构详解·五」链表

1. 链表的定义、构成、术语和性质

链表(Linked list)是一种线性的数据结构。
如图所示,这就是一个链表:
单链表
可以发现,链表的元素之间是指向的关系(这使得我们很容易想到指针)。
每一个元素有两个内容,一个是元素值,一个是其所指向的下一个元素。
元素值为 α \alpha α 的节点是链表的首位,元素值为 δ \delta δ 的节点是链表的末端。
容易看出,最后一个节点指向的是空并且每一个元素都指向其后一个元素。这种链表被称为单链表
有时,我们不让其末端的元素指向空,而是指向第一个元素。这种链表被称为循环单链表
循环单链表
我们现在只让一个元素指向其后一个元素,有一种链表既指向其后一个元素,又指向其前一个元素。这种链表就是双向链表
双向链表
类似地,还有双向循环链表
双向循环链表
有趣的是,数组支持 O ( 1 ) O(1) O(1) 访问元素,但不支持 O ( 1 ) O(1) O(1) 插入删除;但链表恰好相反,支持 O ( 1 ) O(1) O(1) 插入删除,但不支持 O ( 1 ) O(1) O(1) 访问元素。
但是为什么呢?
这是因为,数组预先定义时已经固定好每一个数的前一个和后一个数的位置;而链表不同,对于插入删除,只需要进行改变某数所指向的前一个数和后一个数即可。
以双向链表为例(下同)。
比如要在 p p p 后面插入元素 x x x,那么我们只要将 p p p 所指向的下一个数的上一个数指向 x x x,将 x x x 指向的下一个数指向 p p p 的下一个数,然后将 p p p 指向的下一个数指向 x x x x x x 指向的上一个数指向 p p p 即可。
再比如要删除 p p p,就将 p p p 指向的下一个数的上一个数指向 p p p 的上一个数,再将 p p p 指向的上一个数的下一个数指向 p p p 的下一个数就可以做到删除了。
有些拗口,具体实现在下面代码中。

2. 链表的实现

链表的实现主要有两种:数组模拟和指针模拟(当然,C++ STL 中的 list 也为我们提供了这些功能,不过不常用,在此不再赘述,需要的可以自行搜集)。

2-1. 数组模拟

首先定义一个结构体:

struct node{
	int val,prv,nxt;//val 表示元素值,prv 表示该节点指向的上一个数,nxt 表示节点指向的下一个数
}l[N];
int head,tail,tot//分别表示头指针、尾指针和元素个数

新建链表:

void init()
{
	head=1;
	tail=tot=2;
	l[head].nxt=tail;
	l[tail].prv=head;
}

插入:

void insert(int p,int x)//在 p 后插入元素 x
{
	l[++tot].val=x;
	l[l[p].nxt].prv=tot;
	l[tot].nxt=l[p].nxt;
	l[p].nxt=tot;
	l[tot].prv=p;
}

删除:

void remove(int p)//删除 p
{
	l[l[p].prv].nxt=l[p].nxt;
	l[l[p].nxt].prv=l[p].prv;
}

清空链表:

void clear()
{
	memset(l,0,sizeof(l));
	head=tail=tot=0;
}

2-2. 指针模拟

和数组模拟有些许不同。
定义:

struct node{
	int val;
	node *prv,*nxt;//定义指针
}*head,*tail;//头指针和尾指针

新建链表:

void init()
{
	head=new node();
	tail=new node();//申请使用新的空间
	head->nxt=tail;
	tail->prv=head;
}

插入:

void insert(node *p,int x)//在 p 后插入数据为 x 的新节点
{
	q=new node();
	q->val=x;
	p->nxt->prv=q;
	q->nxt=p->nxt;
	p->nxt=q;
	q->prv=p;
}

删除:

void remove(node *p)
{
	p->prv->nxt=p->nxt;
	p->nxt->prv=p->prv;
	delete p;//回收内存,下同
}

清空(删除)链表:

void clear()
{
	while(head!=tail)
	{
		head=head->nxt;
		delete head->prv;
	}
	delete tail;
}

3. 例题详解

3-1. 洛谷 P1160 队列安排

可以发现,这题还要求我们在一个节点之前插入,然鹅上面的模板并没有。
事实上也很简单,我们只需要转化一下。
对于 p p p 之前插入值为 x x x 的节点,就是在 p p p 的前一个节点的后面插入值为 x x x 的节点。
其他没什么可说的,大家自己实现一下吧。

4. 巩固练习

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值