数据结构与算法--线性表系列(循环链表、双向链表)

hello,everybody,今天我们来学习线性表的最后两种形式,循环链表、双向链表。这两种链表,是链式存储结构的不同形式。书归正传,我们先来看看循环链表吧。

大家思考一个问题,我们把线性表各个元素比作下图的路线图上的城市:

image

我们的线性表各个结点的指针,都是指向唯一的后继结点,线性表的终端结点的指针为空。这样的话,如果我们在南京,我们需要先访问南京右j边的城市,再访问南京左边的城市。根据线性表的结构,我们只能返回上海,从上海依次访问到北京。因为我们的终端结点的指针为空,如果直接访问南京右边的城市,那么到北京后,就断片儿了,也就不能访问南京左边的城市了。现实生活中,如果我们选择这样的路线的话,估计要被大家笑掉大牙了。

这时候,我们的循环链表就出世了。我们把北京与上海连起来,这样就Ok了。其实,就是把线性表的终端结点的指针指向线性表的第一个结点。让线性表,联通起来,成为循环的链表。

循环链表:将单链表中终端结点的指针端由空指针改为指向头结点,就使得整个单链表成为一个环,这种头尾相接的单链表称为单循环链表,简称循环链表(circular linked list).

从刚才的例子中,我们可以发现,循环链表解决了一个严重的问题。它的出现,使得我们可以从当中一个结点遍历整个线性表。

为了使空循环链表与非空循环链表操作一致,我们通常会在线性表中设立空结点。空结点对于循环链表不是必须的,它的出现只是为了统一操作。

image

空的带有头结点的循环链表。

image

非空的带有头结点的循环链表。

其实,单链表与循环链表的区别,在于判断上。单链表是判断p->next不为空,则说明遍历未结束。循环链表判断p->next为头结点,则循环未结束。

在单链表中,我们通过头指针可以花费O【1】的时间复杂度访问到第一个元素结点。但是需要遍历整个链表,花费O【n】的时间复杂度,才可以访问到终端结点。

现在我们可以修改一下链表,使得也可以花费O[1]的时间复杂度访问到终端结点。不同的之处在于,我们不用头指针,而用尾指针来指向终端结点。

image

从上图我们看以看出,通过尾指针rear,我们可以花费O【1】的时间复杂度访问到终端结点。而对于开始结点,其实就是rear->next->next.时间复杂度也为O【1】.

以上就是循环链表,它的出现就是为了解决从中间元素访问整个链表的问题。

我们来看一下双向链表:

还是刚才那幅图:

image

我们在北京开完会,需要走访北京左边的所有城市。根据我们学到的线性结构,只能先到上海, 在由上海开始依次到北京,再从北京到上海。此时,双向链表就诞生了。

单链表查找下个元素的时间复杂度为O【1】,查找上一个元素的时间时间复杂度为O【n】,因为需要我们遍历整个链表。为了克服这个弊端,设计了双向链表。双向链表(double linked list),是在单链表的每个结点中,再设置一个指向前驱结点的指针域。

我们看一下它的代码结构:

typedef struct DulNode

{

     ElemType data;

    struct DulNode *prior ; /*直接前驱指针*/

   struct  DulNode *next;  /*直接后继指针*/

}DulNode,*DuLinkList;

既然单链表有循环链表,那么双向链表也有循环链表。

image

双向链表的循环带头结点的空链表。

image

双向链表的循环带头结点的非空链表

关与它的一些操作,与之前的单链表很相似。

以上就是双向表的知识。

好了,让我们一起来总结一下线性表这章知识吧。

首先,我们先学习了线性表的概念,线性表就是零个或多个具有相同类型的数据元素的有限序列。接下来,我们从存储结构上,分别讲解了线性表的顺序存储,与线性表的链式存储。其中,链式存储结构中,我们又学习了循环链表与双向链表。另外,我们还学习了不用指针来描述链式存储的静态链表。

image

在学习总结线性表这张章时,发现了自己身上很多性格缺点。在写博客文章时,有些浮躁。好多东西,都是复制粘贴,不经过大脑思考。做人,学技术,一定要实在。不能贪图虚名,不能浮躁。

一定要有耐心,有毅力,有决心。在接下来的学习中,一定要锻炼自己这耐心、毅力、决心。

原文地址:https://www.cnblogs.com/VitoCorleone/p/3770552.html