![169a1992061a646eeaec00fed4a81d92.png](https://img-blog.csdnimg.cn/img_convert/169a1992061a646eeaec00fed4a81d92.png)
线性表顺序存储结构的特点是,在逻辑关系上相邻的两个元素在物理位置上也是相邻的,因此可以随机存取表中任一元素。但是,当经常需要做插入和删除操作时,则需要移动大量的元素,而采用链式存储结构就可以避免这些移动。由于链式存储结构存储线性表数据元素的存储空间可能是连续的,也可能是不连续的,所以链表的结构是不可以随机存取的。链式存储是最常用的存储方式之一,不仅可以用来表示线性表,也可以用来表示各种非线性的数据结构。
![db05f0d890ab9485cc43400ae44b9686.png](https://img-blog.csdnimg.cn/img_convert/db05f0d890ab9485cc43400ae44b9686.png)
1、单链表
在使用链式存储结构表示每个数据元素ai时(i是元素的下角标),除了保存ai信息本身以外,还要 一个存储指示其后继元素a(i+1)存储位置的指针,由这两个部分组成元素ai的存储映像通常称之为结点。它包括两个域:存储数据元素的域称为数据域,存储直接后继存储地址的域称为指针域。 即一个结点的存储结构为(data+next)如图:![7e5b9ffe043f8fb24081036684b332c8.png](https://img-blog.csdnimg.cn/img_convert/7e5b9ffe043f8fb24081036684b332c8.png)
![76bfc734e0a5f7efa219757447905e47.png](https://img-blog.csdnimg.cn/img_convert/76bfc734e0a5f7efa219757447905e47.png)
头指针和头节点
头指针:指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。头节点分为两种:- 真实头节点:存储数据
- 虚拟头节点:不存储数据
![10bcffbc636a92b10a3a90b03a617c14.png](https://img-blog.csdnimg.cn/img_convert/10bcffbc636a92b10a3a90b03a617c14.png)
![2311002f8f9bb2a3a368cfa2a3930f70.png](https://img-blog.csdnimg.cn/img_convert/2311002f8f9bb2a3a368cfa2a3930f70.png)
尾指针
尾指针表示该链表最后一个结点的位置,存储的是最后一个结点的地址。尾指针便于从尾部插入数据的效率,有了尾指针可以直接获取最后一位元素的指针域,然后指向新插入的结点,就完成了数据的插入。![09027db561107244d3eed7d5fecbfc2a.png](https://img-blog.csdnimg.cn/img_convert/09027db561107244d3eed7d5fecbfc2a.png)
线性表链式存储结构(LinkedList)类图
由下面的类图我们可以看出,单链表也是实现了List接口,LinkedList为实现类,并增加了一个Node类。![3fc7562bd42819f69b4a42740a3e9df1.png](https://img-blog.csdnimg.cn/img_convert/3fc7562bd42819f69b4a42740a3e9df1.png)
![e2773bca8c716350cb3753de3e3fbcf8.gif](https://img-blog.csdnimg.cn/img_convert/e2773bca8c716350cb3753de3e3fbcf8.gif)
![b62e52e9ba36c23455abf477c1ec0d55.gif](https://img-blog.csdnimg.cn/img_convert/b62e52e9ba36c23455abf477c1ec0d55.gif)
![89259c0885cfa47c447c77d64e75cc15.gif](https://img-blog.csdnimg.cn/img_convert/89259c0885cfa47c447c77d64e75cc15.gif)
![34caecdfff435c0089ac1c82fd7d12d8.gif](https://img-blog.csdnimg.cn/img_convert/34caecdfff435c0089ac1c82fd7d12d8.gif)
![771c8c6e6a2aa6e373a71da22b135b8f.gif](https://img-blog.csdnimg.cn/img_convert/771c8c6e6a2aa6e373a71da22b135b8f.gif)
![e0a39117f1df1d8a308d65fb1fe66130.gif](https://img-blog.csdnimg.cn/img_convert/e0a39117f1df1d8a308d65fb1fe66130.gif)
![db05f0d890ab9485cc43400ae44b9686.png](https://img-blog.csdnimg.cn/img_convert/db05f0d890ab9485cc43400ae44b9686.png)
2、循环链表
循环链表是链式存储结构的另一种形式。它的特点是单链表最后一个结点(终端结点)的指针域不为空,而是指向链表的头结点,使整个链表构成一个环。 循环链表的结点类型与单链表完全相同,在操作上也基本一致,差别仅在于算法中循环的判断条件不再是p或者p->next为空,而是他们是否等于头指针。 如图:![a410f27af81ef7f8df5b5d4f7a4c025c.png](https://img-blog.csdnimg.cn/img_convert/a410f27af81ef7f8df5b5d4f7a4c025c.png)
循环链表实现约瑟夫环
约瑟夫环问题,是一个经典的循环链表问题,题意是:已知 n 个人(分别用编号 1,2,3,…,n 表示)围坐在一张圆桌周围,从编号为 k 的人开始顺时针报数,数到 m 的那个人出列;他的下一个人又从 1 开始,还是顺时针开始报数,数到 m 的那个人又出列;依次重复下去,直到圆桌上剩余一个人。 如图 所示,假设此时圆周周围有 5 个人,要求从编号为 3 的人开始顺时针数数,数到 2 的那个人出列:![c92486da1e046679ce858482b66a9ef8.png](https://img-blog.csdnimg.cn/img_convert/c92486da1e046679ce858482b66a9ef8.png)
出列顺序依次为:
编号为 3 的人开始数 1,然后 4 数 2,所以 4 先出列;
4 出列后,从 5 开始数 1,1 数 2,所以 1 出列;
1 出列后,从 2 开始数 1,3 数 2,所以 3 出列;
3 出列后,从 5 开始数 1,2 数 2,所以 2 出列;
最后只剩下 5 自己,所以 5 胜出。
约瑟夫环问题有多种变形,比如顺时针转改为逆时针等,虽然问题的细节有多种变数,但解决问题的中心思想是一样的,即使用循环链表。
![db05f0d890ab9485cc43400ae44b9686.png](https://img-blog.csdnimg.cn/img_convert/db05f0d890ab9485cc43400ae44b9686.png)
3、双向链表
若是希望从表中快速确定一个结点的直接前驱,只需要在单链表的结点类型中增加一个指向其直接前趋的指针域prior即可,这样就形成链表中有两条不同方向的链,因此成为双向链表。
双向链表的结点表示:
![cb112727eb624cafd9d29f2d9e96aaec.png](https://img-blog.csdnimg.cn/img_convert/cb112727eb624cafd9d29f2d9e96aaec.png)
![fd2c38b2b46e4245b0974238c129da71.png](https://img-blog.csdnimg.cn/img_convert/fd2c38b2b46e4245b0974238c129da71.png)
![9326e8cade36839d023ad7c478d950a5.png](https://img-blog.csdnimg.cn/img_convert/9326e8cade36839d023ad7c478d950a5.png)
![db05f0d890ab9485cc43400ae44b9686.png](https://img-blog.csdnimg.cn/img_convert/db05f0d890ab9485cc43400ae44b9686.png)
4、双向循环链表
其实就是双向链表和循环链表的结合体,对于新的约瑟夫环问题,需要将循环链表和双向链表结合使用,组成:双向循环链表。