操作 | 时间复杂度(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 月 25 日
* 版 本 号:004
*任务描述:针对单循环链表,实现15个基本操作
* 1:头插法建立单循环链表 ;
* 2:尾插法建立单循环链表 ;
* 3:输出单循环链表的元素 ;
* 4:删除单循环链表指定位置的节点 ;
* 5:向单循环链表的指定位置插入节点;
* 6:查找单循环链表指定下标的元素 ;
* 7:求出给定元素在单循环链表中第一次出现的位置;
* 8: 将单循环链表的元素按照升序排序;
* 9:求单循环链表的长度;
* 10:判断当前单循环链表是否为空;
* 11:将单循环链表反转 ;
* 12:求两个单循环链表的并集到第三个单循环链表并按照升序排列(递归版本) ;
* 13:求两个单循环链表的并集到第三个单循环链表并按照升序排列(非递归版本) ;
* 14:清空当前单循环链表 ;
* 15:销毁单循环链表 ;
*主要函数:
* 0:Status InitList(LinkList &L);//每次建立链表之前首先分配链表头结点的内存
* 1:Status InverseCreatList(LinkList L,int listsize);
//头插法输入listsize个元素,建立起有头结点的单循环链表L
* 2:Status OrderCreatList(LinkList L,int listsize);
//尾插法输入listsize个元素,建立起有头结点的单循环链表L
* 3:Status Output(LinkList L);//输出单循环链表
* 4:Status ListDelete(LinkList L,int index);//在带头结点的单循环链表L中,删除第index个元素
* 5:Status ListInsert(LinkList L,int index,ElemType elem);
//在带头结点的单循环链表L中第index个位置之前插入元素elem
* 6:Status GetElem(LinkList L,int index);
//L为带头结点的单循环链表的头指针,若第index个元素存在时,输出其值
* 7:Status GetIndex(LinkList L,ElemType elem);
//L为带头结点的单循环链表的头指针,若元素elem存在时,输出该元素在链表中的第一次//出现的位置
* 8:Status BubbleSort(LinkList L);//冒泡法排序
* 9:int ListLength(LinkList L);//求出单循环链表的长度
* 10:Status IsEmptyList(LinkList L);
//当且仅当头结点和第一个结点均不为空时,单循环链表才存在
* 11:Status ReverseList(LinkList L);//反转单循环链表
* 12:LinkList RecursiveMergeList(LinkList p,LinkList q);
//递归归并单循环链表并返回指向第一个结点的指针
* 13:void NonRecursiveMergeList(LinkList L1,LinkList L2);//非递归归并单循环链表
* 14:void ClearList(LinkList L);//清空链表L的所有节点(头结点外)
* 15:Status DestroyList(LinkList &L);// 销毁单循环链表L
*注意:只有Status InitList(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 LNode
{
ElemType data;
struct LNode * next;
} LNode,*LinkList;
LinkList L1=NULL,L2=NULL;
Status InitList(LinkList &L);//每次建立链表之前首先分配链表头结点的内存
Status InverseCreatList(LinkList L,int listsize);//头插法输入listsize个元素,建立起有头结点的单循环链表L
Status OrderCreatList(LinkList L,int listsize);//尾插法输入listsize个元素,建立起有头结点的单循环链表L
Status Output(LinkList L);//输出单循环链表
Status ListDelete(LinkList L,int index);//在带头结点的单循环链表L中,删除第index个元素
Status ListInsert(LinkList L,int index,ElemType elem);//在带头结点的单循环链表L中第index个位置之前插入元素elem
Status GetElem(LinkList L,int index);//L为带头结点的单循环链表的头指针,若第index个元素存在时,输出其值
Status GetIndex(LinkList L,ElemType elem);//L为带头结点的单循环链表的头指针,若元素elem存在时,输出该元素在链表中的第一次出现的位置
Status BubbleSort(LinkList L);//冒泡法排序
int ListLength(LinkList L);//求出单循环链表的长度
Status IsEmptyList(LinkList L);//当且仅当头结点和第一个结点均不为空时,单循环链表才存在
Status ReverseList(LinkList L);//反转单循环链表
LinkList RecursiveMergeList(LinkList p,LinkList q);//递归归并单循环链表并返回指向第一个结点的指针
void NonRecursiveMergeList(LinkList L1,LinkList L2);//非递归归并单循环链表
void ClearList(LinkList L);//清空链表L的所有节点(头结点外)
Status DestroyList(LinkList &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:
InitList(L2);
cout<<"请输入元素的个数(尾插法建立链表):";
cin>>listsize;
OrderCreatList(L2,listsize);
BubbleSort(L1);
BubbleSort(L2);
L1->next=RecursiveMergeList(L1->next,L2->next);
//该函数返回指向归并后链表的第一个节点,即头结点的后一个节点,将L1变为归并后的链表
cout<<"归并后的链表是:";
Output(L1);
free(L2);//释放L2指针并置为空指针
L2=NULL;
break;
case 13:
InitList(L2);
cout<<"请输入元素的个数(尾插法建立链表):";
cin>>listsize;
OrderCreatList(L2,listsize);
BubbleSort(L1);
BubbleSort(L2);
NonRecursiveMergeList(L1,L2);
cout<<"归并后的链表是:";
Output(L1);
free(L2);
L2=NULL;
break;
case 14:
ClearList(L1);
break;
case 15:
DestroyList(L1);
cout<<"释放内存完毕,销毁单循环链表成功。进行其他操作请先创建单循环链表。\n";
break;
default:
cout<<"请输入正确的操作编号!\n";
break;
}
}
END://释放两个指针的内存
DestroyList(L1);
DestroyList(L2);
return 0;
}
Status InitList(LinkList &L)//每次建立链表之前首先分配链表头结点的内存
{
L=(LinkList)malloc(sizeof(LNode));
if(L)
{
cout<<"分配内存成功。已建立空单循环链表。\n";
L->next=L;//头结点初始化:指向自身
return OK;
}
cout<<"分配内存失败,已退出!\n";
return FALSE;
}
Status InverseCreatList(LinkList L,int listsize)
//头插法输入listsize个元素,建立起有头结点的单循环链表L
//头插法特点:建立的链表元素顺序与输入的顺序相反,每次插入元素都插在头部,较好理解
{
cout<<"请输入元素:";
for(int i=1; i<=listsize; i++)
{
LinkList p=(LinkList)malloc(sizeof(LNode));
cin>>p->data;
p->next=L->next;
L->next=p;
}
cout<<"新链表是:";
Output(L);
return OK;
}
Status OrderCreatList(LinkList L,int listsize)
//尾插法输入listsize个元素,建立起有头结点的单循环链表L
//尾插法特点:建立的链表元素顺序与输入的顺序相同,每次插入元素都插在尾部,细节注释如下
{
LinkList r=L;//必须增加一个尾指针r,使其始终指向当前链表的尾结点
cout<<"请输入元素:";
for(int i=1; i<=listsize; i++)
{
LinkList p=(LinkList)malloc(sizeof(LNode));
cin>>p->data;
r->next=p;
r=p;//更新尾指针
}
r->next=L;//终结链表,指向头结点
cout<<"新链表是:";
Output(L);
return OK;
}
Status Output(LinkList L)//输出单循环链表
{
if(!IsEmptyList(L))//增强程序的鲁棒性
{
LinkList p=L->next;
while(p!=L)//终结循环的条件是当前结点≠头结点
{
cout<<p->data<<" ";
p=p->next;
}
cout<<"\n";
}
else
{
cout<<"链表为空,无法输出。\n";
return ERROR;
}
}
Status ListDelete(LinkList L,int index)
//在带头结点的单循环链表L中,删除第index个元素
{
if(index<1||index>ListLength(L))
{
cout<<"位置越界,操作失败,已退出。\n";
return ERROR;
}
int j=0;//计数器
LinkList p=L;//使之为头结点,而不是第一个节点,若p=L->next,会导致无法删除第一个节点
while(j<=index-2)//当j==index-1,即p为删除位置的前一个结点时退出循环
{
if(p)
{
p=p->next;
j++;
}
else
{
cout<<"位置越界,操作失败,已退出。\n";
return ERROR;
}
}
LinkList q=p->next;//被删除的结点
cout<<"被删除的元素是:"<<q->data<<"\n";
p->next=q->next;
free(q);//释放对应节点的内存
q=NULL;//置空指针,否则q会成为迷途指针,引发错误
cout<<"新链表是:";
Output(L);
return OK;
}
Status ListInsert(LinkList L,int index,ElemType elem)
//在带头结点的单循环链表L中第index个位置之前插入元素elem
{
if(index<1||index>ListLength(L))
{
cout<<"插入位置越界,操作失败,已退出。\n";
return ERROR;
}
int j=0;
LinkList p=L;//原理同删除元素
while(j<=index-2)
{
if(p)
{
p=p->next;
j++;
}
else
{
cout<<"插入位置越界,操作失败,已退出。\n";
return FALSE;
}
}
LinkList newnode=(LinkList)malloc(sizeof(LNode));
newnode->data=elem;
newnode->next=p->next;
p->next=newnode;
cout<<"插入元素成功。新链表是:";
Output(L);
return OK;
}
Status GetElem(LinkList L,int index)
//L为带头结点的单循环链表的头指针,若第index个元素存在时,输出其值
{
if(index<1||index>ListLength(L))
{
cout<<"位置越界,操作错误,已退出。\n";
return FALSE;
}
LinkList p=L;//初始化:p指向头结点
int j=0; //计数器
while(p&&j<index)//顺指针向后查找
{
p=p->next;
j++;
}
cout<<"已找到,位置为"<<index<<"处的元素是:"<< p->data<<"。\n";
return OK;
}
Status GetIndex(LinkList L,ElemType elem)
//L为带头结点的单循环链表的头指针,若元素elem存在时,输出该元素在链表中的第一次出现的位置
{
if(!IsEmptyList(L))
{
int j=1;
LinkList 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(LinkList L)//冒泡法排序
{
if(!IsEmptyList(L))
{
LinkList p=L->next;
while(p!=L)
{
LinkList q=p->next;
while(q!=L)
{
if(q->data<p->data)
{
swap(q->data,p->data);
}
q=q->next;
}
p=p->next;
if(p!=L)
{
q=p->next;
}
}
cout<<"排序后的链表是:";
Output(L);
return OK;
}
cout<<"链表为空,操作错误!已退出!\n";
}
int ListLength(LinkList L)//求出单循环链表的长度
{
int cnt=0;
LinkList p=L;
if(p)
{
p=p->next;
}
while(p!=L)
{
cnt++;
p=p->next;
}
return cnt;
}
Status IsEmptyList(LinkList L)//当且仅当头结点和第一个结点均不为空时,单循环链表才存在
{
if(L!=NULL&&L->next!=L)//必须先检验L是否为NULL,若L==NULL,不事先检查的话,会Runtime Error
{
return FALSE;
}
return TRUE;
}
Status ReverseList(LinkList L)//反转单循环链表
{
if(!IsEmptyList(L))
{
LinkList p1,p2,p3;//使用三个指针
p1=L->next;//p1是当前头结点
p2=L->next->next;
p1->next=L;//头结点变为尾结点
while(p2!=L)
{
p3=p2->next;//预存下一个节点的地址
p2->next=p1;//指向前一个节点
p1=p2;//更新指针
p2=p3;
}
L->next=p1;//完成转置
cout<<"转置后的链表为:";
Output(L);
return OK;
}
cout<<"链表为空,无法转置,已退出。\n";
return FALSE;
}
LinkList RecursiveMergeList(LinkList p,LinkList q)//递归归并单循环链表并返回指向第一个结点的指针
{
LinkList r;//新链表的结点
while(p!=L1&&q!=L2)
{
if(p->data<=q->data)
{
r=p;
r->next=RecursiveMergeList(p->next,q);//递归求出后续的节点
}
else
{
r=q;
r->next=RecursiveMergeList(p,q->next);
}
return r;//返回指向新节点的指针
}
if(q==L2)//当第二条链表先归并完,那么直接返回L1的剩余第一个结点的指针即可(最终能够链接回到L1的头结点)
{
return p;
}
if(q!=L2)
/*
当第二条链表没归并完,必须得一个个结点的递归,直至q==L2,返回指针p(此时p==L1),
否则归并后的链表的最后一个结点并不指向L1的头结点(我们目的是将L1变为归并后的链表),
这样输出L1的话,会形成死循环,无法判断终结的结点。
*/
{
r=q;
r->next=RecursiveMergeList(p,q->next);
}
}
void NonRecursiveMergeList(LinkList L1,LinkList L2)//非递归归并单循环链表
{
LinkList p1,p2,p3;
p1=L1->next;//第一条链表指针
p2=L2->next;//第一条链表指针
p3=L1;//归并后链表的头指针,使之为L1的头指针,即将L1变为归并后的链表
while(p1!=L1&&p2!=L2)//当某一条链表归并完成后退出
{
if(p1->data<=p2->data)
{
p3->next=p1;
p3=p1;
p1=p1->next;
}
else
{
p3->next=p2;
p3=p2;
p2=p2->next;
}
}
if(p2==L2)
{
p3->next=L1;
return;
}
while(p2!=L2)
{
p3->next=p2;
p3=p2;
p2=p2->next;
}
p3->next=L1;//道理同递归归并的版本
}
void ClearList(LinkList L)//清空链表L的所有节点(头结点外)
{
if(!IsEmptyList(L))
{
LinkList p=L->next;
DestroyList(p);//销毁(释放)头结点后所有结点的内存
L->next=L;//置空
cout<<"清空单循环链表成功。\n";
}
else
{
cout<<"链表本身为空,无需清空!\n";
}
}
Status DestroyList(LinkList &L)// 销毁单循环链表L
{
if(!IsEmptyList(L))//必须先检验,防止L==NULL
{
LinkList p=L->next;
free(L);//释放指针所指向的内存后,指针本身并不改变
while(p!=L)
{
LinkList 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";
cout<<"15:销毁单链表 ;\n";
}