链表内容的补充3:循环链表与双向链表

1.循环链表

  与单链表的整体结构类似,循环链表也是一种链式存储的结构,不同的是:循环链表要求表中最后一个结点的指针指向链表的头结点,达到一个循环的效果。由此可以得出,遍历循环链表指针数据域的语句是指针所指结点或指针所指结点的后继结点是否等于头结点(或指针是否等于头指针)。

  除了链表结构不同以外,其余的基本操作与单链表几乎一致,包括新建元素,增加元素,删除元素等,示意图如下所示:

循环链表示意图

2.双向链表 

 不同于普通的单链表,双向链表设置了两个指针Prior与Next,prior指针方便直接指示前驱,而next指针仍然指示后继节点。设置两个指针最直观的优势在于,方便了链表的双向访问,即不需要通过计算for循环的次数来访问结点的前驱,而仅需要p->prior语句即可。

(1)双向链表结点的基本结构

与单链表结点的结构类似,双向链表的结点结构也具有数据域与指针域,数据域由int类型的变量data进行表示,该类型也可以是string,char等其他类型,不同的是,指针域需要定义两个指针,分别指示直接前驱结点以及直接后继结点,通过前驱指针域的设立可以直接访问前驱结点。双向链表头尾结点的前驱与后继也值得注意:头结点的前驱是链表的尾结点,后继则是位序为1的普通节点,而尾结点的前驱结点是其逻辑位序上的前驱结点,相对地,其后继节点不为NULL,是整个双向链表的头结点。结点的结构代码以及示意图如下:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
typedef struct DuNode
{
    int data;
    struct DuNode *next;
    struct DuNode *prior;
}Dulnode,*Dulinklist;
双向链表示意图

在定义了基本结构后,按单链表的学习思路,需要完善双向链表结点的不同操作。双向链表的搜索结点,定位结点,求链表长度操作与单链表相同,不同之处主要在于插入以及删除操作。

(2)双向链表的初始化操作

 双向链表的初始化操作较为简单,按双向链表的结点结构来看。主要步骤如下:1)定义一个Dulinklist类型的头指针head,使用malloc语句为其分配空间    2)head指针指向的结点前驱结点定义为自身,后继结点定义为自身(如果没有该初始化的步骤会导致无法进行后续的插入结点工作)    初始化操作代码如下:

Dulinklist Init()
{
    Dulinklist head;
    head=(Dulnode*)malloc(sizeof(Dulnode));
    head->prior=head;
    head->next=head;
    return head;
}

(3)双向链表的结点插入操作

与单链表的插入操作类似,首先明确操作的整体过程,即在第i个结点之前插入全新的结点,i=1,2,....n,在整体过程了解之后,基本思路如下:(1)定义头指针head,双向链表结点的指针p,先由头指针开始向后移动i次,寻找到位序为i+1的结点 ,以指针p指向该段内存 (2)定义int 型的变量为e,用一个全新的指针s表示该结点,利用malloc语句,新建一个s指针指向的结点。该节点的数据域赋值为e。   (3)s结点的前驱结点赋值为p结点的前驱结点,p前驱结点的后继结点指向s,同样地,s结点的后继结点赋为p,p结点的前驱结点记作s  在位序为i的结点之前插入结点的操作基本代码如下:

void Insert(Dulinklist head,int i,int e)
{
    Dulinklist p,s;
    p=head;
    for(int j=0;j<i-1;j++)
    {
       p=p->next;
    }
    s=(Dulnode*)malloc(sizeof(Dulnode));
    s->data=e;
    p->prior->next=s;
    s->prior=p->prior;
    s->next=p;
    p->prior=s;
}

实现插入功能的案例代码如下:

int main()
{
    Dulinklist head,p;
    head=Init();
    int m;
    cin>>m;
    int num[m];
    for(int j=0;j<m;j++)
    {
        cin>>num[j];
    }
    for(int l=1;l<=m;l++)
    {
        Insert(head,l+1,num[l-1]);
    }
    p=head->next;
    while(p!=head)
    {
        cout<<p->data<<" ";
        p=p->next;
    }
    return 0;
}

 (4)双向链表结点的删除操作

与双向链表插入结点的操作类似,先明确删除结点的整体过程,即定义一个指针p,p初始化为头指针,通过for循环使p指向位序为i的结点,从而实现接下来的指针移动步骤。删除单个结点的基本思路如下:1)定义int类型的变量为e,将结点数据域的值赋给该变量,输出该值  2)将p指向结点的前驱结点的后继结点赋值为p的后继结点,p的后继结点的前驱结点赋值为p的前驱结点 3)释放指针p所指结点p的空间,完成结点的删除   具体代码如下:

void Delete(Dulinklist head,int e,int i)
{
    Dulinklist p;
    p=head;
    for(int k=0;k<=i-1;k++)
    {
        p=p->next;
    }
    e=p->data;
    cout<<e<<endl;
    p->prior->next=p->next;
    p->next->prior=p->prior;
    free(p);
}

猜你喜欢

转载自blog.csdn.net/qq_38846633/article/details/81283660