线性表链式存储(双向循环链表)及其14种操作的实现

本文介绍双向循环链表的基本操作及其实现,包括建立、输出、删除、插入等,并分析了各种操作的时间复杂度和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

操作

时间复杂度(T(n))

空间复杂度(S(n))

判断是否为空

O(1)

O(1)

得到长度

O(n)

O(1)

转置链表

O(n)

O(1)

得到指定下标的元素

O(n)

O(1)

得到指定元素的下标

O(n)

O(1)

插入元素(只告诉插入到第几个结点之前,不告诉内存地址)

O(n)

搜索到指定下标的时间为O(n),更换指针指向的时间为O(1)

O(1)

需要开辟1个内存空间,即使插入1000个,也是O(1),因为需要的新内存空间是常数的,不是线性的

删除元素(只告诉删除第几个结点,不告诉地址)

O(n)

同上

O(1)

道理同插入元素

冒泡排序

O(n^2)

O(1)

道理同转置链表

将两个已经有序的链表(长度分别n,m)的合并到第一个链表,且保持新表有序

O(n+m)

虽然O(n+m)与O(n)同为线性阶,但是当m远大于n时,两者的差别会较大,写成O(n+m)更加准确

O(1)

不需要开辟新的内存空间,只需要改变指针的指向

/*   数据结构分析与学习专栏
*  Copyright (c) 2015, 山东大学计算机科学与技术专业学生
*  All rights reserved.
*   作    者:   高祥
*   完成日期:  2015 年 3 月 26 日
*   版 本 号:005
 
*任务描述:针对双向循环链表,实现15个基本操作
*    1:头插法建立双向循环链表;
*    2:尾插法建立双向循环链表;
*    3:输出双向循环链表的元素;
*    4:删除双向循环链表指定位置的节点;
*    5:向双向循环链表的指定位置插入节点;
*    6:查找双向循环链表指定下标的元素;
*     7:求出给定元素在双向循环链表中第一次出现的位置;
*    8:将双向循环链表的元素按照升序排序;
*    9:求双向循环链表的长度;
*    10:判断当前双向循环链表是否为空;
*    11:将双向循环链表反转;
*    12:求两个双向循环链表的并集到第三个双向循环链表并按照升序排列  ;
*    13:清空当前双向循环链表;
*    14:销毁双向循环链表  ;
 
*主要函数:
*   0:StatusInitList(LinkList &L);//每次建立链表之前首先分配链表头结点的内存
*   1:StatusInverseCreatList(LinkList L,int listsize);
   //头插法输入listsize个元素,建立起有头结点的双向循环链表L
*   2:StatusOrderCreatList(LinkList L,int listsize);
   //尾插法输入listsize个元素,建立起有头结点的双向循环链表L
*   3:StatusOutput(LinkList L);//输出双向循环链表
*   4:StatusListDelete(LinkList L,int index);//在带头结点的双向循环链表L中,删除第index个元素
*   5:StatusListInsert(LinkList L,int index,ElemType elem);
   //在带头结点的双向循环链表L中第index个位置之前插入元素elem
*   6:StatusGetElem(LinkList L,int index);
   //L为带头结点的双向循环链表的头指针,若第index个元素存在时,输出其值
*   7:StatusGetIndex(LinkList L,ElemType elem);
   //L为带头结点的双向循环链表的头指针,若元素elem存在时,输出该元素在链表中的第一次//出现的位置
*   8:StatusBubbleSort(LinkList L);//冒泡法排序
*   9:intListLength(LinkList L);//求出双向循环链表的长度
*  10:Status IsEmptyList(LinkList L);
   //当且仅当头结点和第一个结点均不为空时,双向循环链表才存在
*  11:Status ReverseList(LinkList L);//反转双向循环链表
*  12:void NonRecursiveMergeList(LinkList L1,LinkList L2);//非递归归并双向循环链表
*  13:void ClearList(LinkList L);//清空链表L的所有节点(头结点外)
*  14:Status DestroyList(LinkList &L);// 销毁单循环链表L
 
*注意:只有StatusInitList(LinkList &L)和Status DestroyList(LinkList &L)必须使用引用传参数
(对main函数中指针本身的修改),其余的函数没有必要将形参声明为引用。
*/
 
#include<iostream>
#include <cstdlib>
#include<algorithm>
using namespace std;
 
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
 
typedef int Status;//Status是函数的类型,其值是函数结果状态的代码
typedef int ElemType;//ElemType是数据元素的类型,使用时用户自行定义
 
typedef struct DuLNode
{
   ElemType data;
   struct DuLNode * next;
   struct DuLNode * prior;
} DuLNode,*DuLinkList;
 
DuLinkList L1=NULL,L2=NULL;
 
Status InitList(DuLinkList &L);//每次建立链表之前首先分配链表头结点的内存
 
Status InverseCreatList(DuLinkList L,intlistsize);//头插法输入listsize个元素,建立起有头结点的双向循环链表L
 
Status OrderCreatList(DuLinkList L,intlistsize);//尾插法输入listsize个元素,建立起有头结点的双向循环链表L
 
Status Output(DuLinkList L);//输出双向循环链表
 
Status ListDelete(DuLinkList L,intindex);//在带头结点的双向循环链表L中,删除第index个元素
 
Status ListInsert(DuLinkList L,intindex,ElemType elem);//在带头结点的双向循环链表L中第index个位置之前插入元素elem
 
Status GetElem(DuLinkList L,int index);//L为带头结点的双向循环链表的头指针,若第index个元素存在时,输出其值
 
Status GetIndex(DuLinkList L,ElemTypeelem);//L为带头结点的双向循环链表的头指针,若元素elem存在时,输出该元素在链表中的第一次出现的位置
 
Status BubbleSort(DuLinkList L);//冒泡法排序
 
int ListLength(DuLinkList L);//求出双向循环链表的长度
 
Status IsEmptyList(DuLinkList L);//当且仅当头结点和第一个结点均不为空时,双向循环链表才存在
 
Status ReverseList(DuLinkList L);//反转双向循环链表
 
void NonRecursiveMergeList(DuLinkListL1,DuLinkList L2);//非递归归并双向循环链表
 
void ClearList(DuLinkList L);//清空链表L的所有节点(头结点外)
 
Status DestroyList(DuLinkList &L);// 销毁双向循环链表L
 
void Interaction();//输出操作
 
int main()
{
   Interaction();
 
   int operate;
   int listsize;
 
   while(cin>>operate)
    {
       switch (operate)
       {
       case 0:
           goto END;
 
       case 1:
           if(InitList(L1))
           {
                cout<<"请输入链表的大小:";
                int listsize;
                cin>>listsize;
                InverseCreatList(L1,listsize);
           }
           break;
 
       case 2:
           if(InitList(L1))
           {
                cout<<"请输入链表的大小:";
                cin>>listsize;
                InitList(L1);
                OrderCreatList(L1,listsize);
           }
           break;
 
       case 3:
           Output(L1);
           break;
 
       case 4:
           cout<<"请输入要删除元素的位置:";
           int index;
           cin>>index;
           ListDelete(L1,index);
           break;
 
       case 5:
           cout<<"请输入要插入的位置和元素的大小:";
           ElemType elem;
           cin>>index>>elem;
           ListInsert(L1,index,elem);
           break;
 
       case 6:
           cout<<"请输入要查找的元素的位置:";
           cin>>index;
           GetElem(L1,index);
           break;
 
       case 7:
           cout<<"请输入要查找的元素:";
           cin>>elem;
           GetIndex(L1,elem);
           break;
 
       case 8:
           BubbleSort(L1);
           break;
 
       case 9:
           cout<<"双向循环链表的长度是:"<<ListLength(L1)<<endl;
           break;
 
       case 10:
           if(!IsEmptyList(L1))
           {
                cout<<"当前双向循环链表不为空。\n";
           }
           else
           {
                cout<<"当前双向循环链表为空。\n";
           }
           break;
 
       case 11:
           ReverseList(L1);
           break;
 
       case 12:
           if(!IsEmptyList(L1))
           {
                InitList(L2);
                cout<<"请输入元素的个数(尾插法建立链表):";
                cin>>listsize;
                OrderCreatList(L2,listsize);
 
                BubbleSort(L1);
                BubbleSort(L2);
 
                NonRecursiveMergeList(L1,L2);
                cout<<"归并后的链表是:";
                Output(L1);
 
                free(L2);
                L2=NULL;
           }
           else
           {
                cout<<"请先建立第一个链表。\n";
           }
           break;
 
       case 13:
           ClearList(L1);
           break;
 
       case 14:
           DestroyList(L1);
           cout<<"释放内存完毕,销毁双向循环链表成功。进行其他操作请先创建双向循环链表。\n";
           break;
 
       default:
           cout<<"请输入正确的操作编号!\n";
           break;
       }
    }
 
END://释放两个指针的内存
   DestroyList(L1);
   DestroyList(L2);
   return 0;
}
 
Status InitList(DuLinkList &L)//每次建立链表之前首先分配链表头结点的内存
{
   L=(DuLinkList)malloc(sizeof(DuLNode));
   if(L)
    {
       cout<<"分配内存成功。已建立空双向循环链表。\n";
       L->prior=L;//头结点的两个指针均指向自身
       L->next=L;
       return OK;
    }
   cout<<"分配内存失败,已退出!\n";
   return FALSE;
}
 
Status InverseCreatList(DuLinkList L,intlistsize)
//头插法输入listsize个元素,建立起有头结点的双向循环链表L
//头插法特点:建立的链表元素顺序与输入的顺序相反,每次插入元素都插在头部,较好理解
{
   cout<<"请输入元素:";
   for(int i=1; i<=listsize; i++)
    {
       DuLinkList p=(DuLinkList)malloc(sizeof(DuLNode));
       cin>>p->data;
       p->next=L->next;
       p->prior=L;
 
       if(L->next!=L)
       {
           L->next->prior=p;//已经存在结点
       }
       else
       {
           L->prior=p;
//当目前链表中没有结点时,将头结点的指针指向将要插入的第一个节点,此后,尾结点(头结点的前驱)不再改变
       }
 
       L->next=p;//完成插入节点
    }
 
   cout<<"新链表是:";
   Output(L);
   return OK;
}
 
Status OrderCreatList(DuLinkList L,intlistsize)
//尾插法输入listsize个元素,建立起有头结点的双向循环链表L
//尾插法特点:建立的链表元素顺序与输入的顺序相同,每次插入元素都插在尾部,细节注释如下
{
   DuLinkList r=L;//必须增加一个尾指针r,使其始终指向当前链表的尾结点
   cout<<"请输入元素:";
   for(int i=1; i<=listsize; i++)
    {
       DuLinkList p=(DuLinkList)malloc(sizeof(DuLNode));
       cin>>p->data;
       p->prior=r;
        p->next=r->next;
       r->next=p;
       r=p;//更新尾指针
       L->prior=r;//形成双向循环链表
       r->next=L;
    }
 
   cout<<"新链表是:";
   Output(L);
   return OK;
}
 
Status Output(DuLinkList L)//输出双向循环链表
{
   if(!IsEmptyList(L))//增强程序的鲁棒性
    {
       DuLinkList p=L->next;
       while(p!=L)//终结循环的条件是当前结点≠头结点
       {
           cout<<p->data<<" ";
           p=p->next;
       }
       cout<<"\n";
    }
   else
    {
       cout<<"链表为空,无法输出。\n";
       return ERROR;
    }
}
 
Status ListDelete(DuLinkList L,int index)
//在带头结点的双向循环链表L中,删除第index个元素
{
   if(index<1||index>ListLength(L))
    {
       cout<<"位置越界,操作失败,已退出。\n";
       return ERROR;
    }
 
   int j=0;//计数器
   DuLinkList p=L;//使之为头结点,而不是第一个节点,若p=L->next,会导致无法删除第一个节点
   while(j<=index-2)//当j==index-1,即p为删除位置的前一个结点时退出循环
    {
       if(p)
       {
           p=p->next;
           j++;
       }
       else
       {
           cout<<"位置越界,操作失败,已退出。\n";
           return ERROR;
       }
    }
   DuLinkList q=p->next;//被删除的结点
   cout<<"被删除的元素是:"<<q->data<<"\n";
 
   p->next=q->next;//修改两个指针
   q->next->prior=q->prior;
 
   free(q);//释放对应节点的内存
   q=NULL;//置空指针,否则q会成为迷途指针,引发错误
   cout<<"新链表是:";
   Output(L);
   return OK;
}
 
Status ListInsert(DuLinkList L,intindex,ElemType elem)
//在带头结点的双向循环链表L中第index个位置之前插入元素elem
{
   if(index<1||index>ListLength(L))
    {
       cout<<"插入位置越界,操作失败,已退出。\n";
       return ERROR;
    }
 
   int j=0;
   DuLinkList p=L;//原理同删除元素
   while(j<=index-2)
    {
       if(p)
       {
           p=p->next;
           j++;
       }
       else
       {
           cout<<"插入位置越界,操作失败,已退出。\n";
           return FALSE;
       }
    }
 
   DuLinkList newnode=(DuLinkList)malloc(sizeof(DuLNode));
   newnode->data=elem;
 
    newnode->next=p->next;//修改四个指针
   newnode->prior=p;
   p->next->prior=newnode;
   p->next=newnode;
 
   cout<<"插入元素成功。新链表是:";
   Output(L);
   return OK;
}
 
Status GetElem(DuLinkList L,int index)
//L为带头结点的双向循环链表的头指针,若第index个元素存在时,输出其值
{
   if(index<1||index>ListLength(L))
    {
       cout<<"位置越界,操作错误,已退出。\n";
       return FALSE;
    }
 
   DuLinkList p=L;//初始化:p指向头结点
   int j=0; //计数器
   while(p&&j<index)//顺指针向后查找
    {
       p=p->next;
       j++;
    }
   cout<<"已找到,位置为"<<index<<"处的元素是:"<<p->data<<"。\n";
   return OK;
}
 
Status GetIndex(DuLinkList L,ElemType elem)
//L为带头结点的双向循环链表的头指针,若元素elem存在时,输出该元素在链表中的第一次出现的位置
{
   if(!IsEmptyList(L))
    {
       int j=1;
       DuLinkList p=L->next;
       while(p!=L)
       {
           if(p->data==elem)
           {
                cout<<"元素"<<elem<<"是双向循环链表中的第"<<j<<"个元素。\n";
                return OK;
           }
           j++;
           p=p->next;
       }
       cout<<"元素"<<elem<<"不在双向循环链表中。\n";
       return FALSE;
    }
   else
    {
       cout<<"双向循环链表为空,无法查找,已退出。\n";
       return FALSE;
    }
}
 
Status BubbleSort(DuLinkList L)//冒泡法排序
{
   if(!IsEmptyList(L))
    {
       DuLinkList p=L->next;
       while(p!=L)
       {
           DuLinkList q=p->next;
           while(q!=L)
           {
                if(q->data<p->data)
                {
                   swap(q->data,p->data);
                }
                q=q->next;
           }
           p=p->next;
       }
 
       cout<<"排序后的链表是:";
       Output(L);
       return OK;
    }
   cout<<"链表为空,操作错误!已退出!\n";
}
 
int ListLength(DuLinkList L)//求出双向循环链表的长度
{
   int cnt=0;
   DuLinkList p=L;
   if(p)
    {
       p=p->next;
    }
   while(p!=L)
    {
       cnt++;
       p=p->next;
    }
   return cnt;
}
 
Status IsEmptyList(DuLinkList L)//当且仅当头结点和第一个结点均不为空时,双向循环链表才存在
{
   if(L!=NULL&&L->next!=L&&L->prior!=L)//必须先检验L是否为NULL,若L==NULL,不事先检查的话,会RuntimeError
    {
       return FALSE;
    }
   return TRUE;
}
 
Status ReverseList(DuLinkList L)//反转双向循环链表
{
   if(!IsEmptyList(L))
    {
       DuLinkList p=L->next;
       while(p!=L)
       {
           DuLinkList q=p->next;//一定先将将要交换自身指针的结点的下一个结点预存起来,否则易出错
           swap(p->next,p->prior);//交换自身结点
           p=q;//更新未交换的结点
       }
       swap(L->next,L->prior);//交换头结点的指针
 
       cout<<"转置后的链表为:";
       Output(L);
       return OK;
    }
   cout<<"链表为空,无法转置,已退出。\n";
   return FALSE;
}
 
 
void NonRecursiveMergeList(DuLinkListL1,DuLinkList L2)//非递归归并双向循环链表
//递归归并双向循环链表十分繁琐,此处不再列出
{
    DuLinkListp1,p2,p3,q,end1,end2;
   p1=L1->next;//第一条链表指针
   p2=L2->next;//第一条链表指针
   end1=L1->prior;//将链表的尾结点指针预存起来,便于后续操作
   end2=L2->prior;
   p3=L1;//归并后链表的头指针,使之为L1的头指针,即将L1变为归并后的链表
 
   while(p1!=L1&&p2!=L2)//当某一条链表归并完成后退出
    {
       if(p1->data<=p2->data)
       {
           q=p1->next;//预存下一个将要归并的节点指针
           p1->next=L1;//修改四个指针
           p3->next=p1;
           p1->prior=p3;
           L1->prior=p1;
           p3=p1;//更新指针
           p1=q;
       }
       else
       {
           q=p2->next;//同上
           p2->next=L1;
           p3->next=p2;
           p2->prior=p3;
           L1->prior=p2;
           p3=p2;
           p2=q;
       }
    }
 
   if(p1!=L1)//L1链表未归并完成,L2链表已经归并完成,只需要修改3个指针
    {
       p3->next=p1;
       p1->prior=p3;
       L1->prior=end1;//形成循环
    }
 
   if(p2!=L2)//L1链表归并完成,L2链表未归并完成,需要修改4个指针
    {
       p3->next=p2;
       p2->prior=p3;
       L1->prior=end2;
       end2->next=L1;
    }
 
   cout<<"归并后的双向循环链表是:";
   Output(L1);
}
 
void ClearList(DuLinkList L)//清空链表L的所有节点(头结点外)
{
   if(!IsEmptyList(L))
    {
       DuLinkList p=L->next;
       DestroyList(p);//销毁(释放)头结点后所有结点的内存
       L->next=L;//置空
       L->prior=L;
       cout<<"清空双向循环链表成功。\n";
    }
   else
    {
       cout<<"链表本身为空,无需清空!\n";
    }
}
 
Status DestroyList(DuLinkList &L)// 销毁双向循环链表L
{
   if(!IsEmptyList(L))//必须先检验,防止L==NULL
    {
       DuLinkList p=L->next;
       free(L);//释放指针所指向的内存后,指针本身并不改变
       while(p!=L)
       {
           DuLinkList q=p->next;
           free(p);
           p=q;
       }
    }
   L=NULL;
   return OK;
}
 
void Interaction()//输出操作
{
   cout<<"请输入对应操作的序号:\n";
   cout<<"0:退出程序;\n";
   cout<<"1:头插法建立双向循环链表;\n";
   cout<<"2:尾插法建立双向循环链表;\n";
   cout<<"3:输出双向循环链表的元素;\n";
   cout<<"4:删除双向循环链表指定位置的节点;\n";
   cout<<"5:向双向循环链表的指定位置插入节点;\n";
   cout<<"6:查找双向循环链表指定下标的元素;\n";
   cout<<"7:求出给定元素在双向循环链表中第一次出现的位置;\n";
   cout<<"8:将双向循环链表的元素按照升序排序;\n";
   cout<<"9:求双向循环链表的长度;\n";
   cout<<"10:判断当前双向循环链表是否为空;\n";
   cout<<"11:将双向循环链表反转;\n";
   cout<<"12:求两个双向循环链表的并集到第三个双向循环链表并按照升序排列;\n";
   cout<<"13:清空当前双向循环链表;\n";
   cout<<"14:销毁双向循环链表  ;\n";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值