目录
扫描二维码关注公众号,回复:
14814027 查看本文章
前言
本文参考王卓老师的数据结构视频和严蔚敏老师的《数据结构》
一、双向链表的定义
用一组地址任意的存储单元存放线性表中的数据元素。
结点 = 数据元素+指针(指后继元素存储位置)
所谓双向链表,即有两个指针,分别指向前驱和后继元素
二、双向链表的 C++ 语言描述
代码如下(示例):
typedef struct Lnode {
int data;
struct Lnode* prior, * next;//
}DuLnode,*DuLinkList;
三、链表中基本操作的实现
3.1构造一个不带头结点的空双向链表
代码如下(示例):
void InitList(DuLinkList& L)
{
L=NULL;
}
3.2头插法建立双向链表
时间复杂度:O(n) 逆序输入
//头插法建立双向链表 时间复杂度:O(n) 逆序输入
void CreateList_F(DuLinkList& L, int t[],int n)
{
//创建n个节点
if(!L)
{
DuLnode* p =new DuLnode;
p->data=t[n-1];
//p->next=NULL;
L=p;
L->prior=NULL;
L->next=NULL;
//p->next=NULL;
//p->prior=NULL;
}
for(int i=n-2;i>=0;i--)
{
DuLnode* p =new DuLnode;
p->data=t[i];
p->prior=NULL;
p->next=L;
L->prior=p;
L=p;
}
}
3.3尾插法建立双向链表
正序输入 时间复杂度:O(n)
void CreateList_L(DuLinkList &L,int a[],int n)
{
DuLnode *r=L;
for(int i=0;i<n;i++)
{
DuLnode *p=new DuLnode;
p->data=a[i];
if(!L)
{
//p->next=NULL;
L=p;
L->next=NULL;
L->prior=NULL;
r=p;
}
else
{
r->next=p;
p->next=NULL;
p->prior=r;
r=p;
}
}
}
3.3求双向链表长度
用一个int型的length来计算,在循环中,当tmp未走到空的时候,每次都加1
int ListLength(DuLinkList L)
{
int length=0;
DuLnode *tmp=L;
while(tmp!=NULL)
{
length++;
tmp=tmp->next;
}
return length;
}
3.4求第i个元素的值
DuLnode* GetElem(DuLinkList L, int i)
{
int j=1;
DuLnode *p=L;
if(i==1&&ListLength(L)==1)
return L;
else
{
while(p&&j<i)
{
j++;
p=p->next;
}
if(j==i)
return p;
else return NULL;
}
}
3.5双向链表的插入
插入在p的前面
void LinkList_Dul(DuLinkList& L, int i, int e)
{
DuLnode* p=L;
if (!(p = GetElem(L, i))) printf("未找到\n");
else
{
DuLnode* s = new DuLnode;
s->data = e;
if(p==L)
{
s->next=L;
s->prior=NULL;
L->prior=s;
L=s;
}
else
{
s->prior = p->prior;
p->prior->next = s;
s->next = p;
p->prior = s;
}
}
}
3.6双向链表的删除
void istDelte_Dul(DuLinkList& L, int i, int &e)
{
DuLnode* p;
if (!(p = GetElem(L, i))) printf("未找到\n");
else
{
e = p->data;
if(ListLength(L)==1)
{
delete p;
L=NULL;
}
else
{
if(p==L)
{
L=L->next;
L->prior=NULL;
//delete p;
}
else if(!p->next)
{
p->prior->next=NULL;
}
else
{
p->prior->next = p->next;
p->next->prior = p->prior;
}
delete p;
}
}
}
3.7遍历
void ListTraverse(DuLinkList L)
{
DuLnode *tmp=L;
int i=0;
while(tmp!=NULL)
{
cout<<"第"<<i+1<<"个元素的数据:"<<tmp->data<<endl;
i++;
tmp=tmp->next;
}
}
3.8清空链表
在不带头节点的双向链表中,清空双向链表就是销毁链表
void CleanList(DuLinkList& L)
{
//DuLnode *tmp=L;
DuLnode *p=NULL;
/*while(tmp)
{
if(ListLength(tmp)==1)
{
cout<<"tttt"<<endl;
tmp=tmp->next;
delete L;
L=NULL;
}
else
{
p=tmp;
tmp=tmp->next;
delete p;
}
}
*/
while(L)
{
p=L;
L=L->next;
delete p;
}
L=NULL;
}
3.9判断链表是否为空
为空返回1
int isEmpty(DuLinkList L)
{
if(!L) return 1;//为空返回1
else return 0;
}
3.10按值查找,返回地址
时间效率分析:查找次数与位置有关,O(n)
Lnode* LocationElem(DuLinkList L, int e)
{
DuLnode* p = L;
while (p && p->data != e)
{
p = p->next;
}
return p;
}
3.11按值查找,返回位置序号
int LocateElem_L(DuLinkList L, int e)
{
int j = 1;
DuLnode* p = L;
while (p && p->data != e)
{
p = p->next;
j++;
}
if (p) return j;
else return 0;
}
四、代码实现
#include <iostream>
using namespace std;
const int N=100;
typedef struct Lnode {
int data;
struct Lnode* prior, * next;//
}DuLnode,*DuLinkList;
/*
双向单链表:头节点的前驱指针为空,尾节点的后继指针也为空
双向循环链表:头节点的前驱指针指向尾节点,尾节点的后继指针指向头节点
双向循环链表具有对称性
p->next->prior=p=p->prior->next;
*/
//双向链表的初始化
void InitList(DuLinkList& L)
{
L=NULL;
}
//头插法建立单链表 时间复杂度:O(n) 逆序输入
void CreateList_F(DuLinkList& L, int t[],int n)
{
//创建n个节点
if(!L)
{
DuLnode* p =new DuLnode;
p->data=t[n-1];
//p->next=NULL;
L=p;
L->prior=NULL;
L->next=NULL;
//p->next=NULL;
//p->prior=NULL;
}
for(int i=n-2;i>=0;i--)
{
DuLnode* p =new DuLnode;
p->data=t[i];
p->prior=NULL;
p->next=L;
L->prior=p;
L=p;
}
}
//尾插法 正序输入 时间复杂度:O(n)
void CreateList_L(DuLinkList &L,int a[],int n)
{
DuLnode *r=L;
for(int i=0;i<n;i++)
{
DuLnode *p=new DuLnode;
p->data=a[i];
if(!L)
{
//p->next=NULL;
L=p;
L->next=NULL;
L->prior=NULL;
r=p;
}
else
{
r->next=p;
p->next=NULL;
p->prior=r;
r=p;
}
}
}
//求长度
int ListLength(DuLinkList L)
{
int length=0;
DuLnode *tmp=L;
while(tmp!=NULL)
{
length++;
tmp=tmp->next;
}
return length;
}
//
//求第i个元素的值
DuLnode* GetElem(DuLinkList L, int i)
{
int j=1;
DuLnode *p=L;
if(i==1&&ListLength(L)==1)
return L;
else
{
while(p&&j<i)
{
j++;
p=p->next;
}
if(j==i)
return p;
else return NULL;
}
}
//双向链表的插入
//插入在p的前面
void LinkList_Dul(DuLinkList& L, int i, int e)
{
DuLnode* p=L;
if (!(p = GetElem(L, i))) printf("未找到\n");
else
{
DuLnode* s = new DuLnode;
s->data = e;
if(p==L)
{
s->next=L;
s->prior=NULL;
L->prior=s;
L=s;
}
else
{
s->prior = p->prior;
p->prior->next = s;
s->next = p;
p->prior = s;
}
}
}
//双向链表的删除
void istDelte_Dul(DuLinkList& L, int i, int &e)
{
DuLnode* p;
if (!(p = GetElem(L, i))) printf("未找到\n");
else
{
e = p->data;
if(ListLength(L)==1)
{
delete p;
L=NULL;
}
else
{
if(p==L)
{
L=L->next;
L->prior=NULL;
//delete p;
}
else if(!p->next)
{
p->prior->next=NULL;
}
else
{
p->prior->next = p->next;
p->next->prior = p->prior;
}
delete p;
}
}
}
//遍历
void ListTraverse(DuLinkList L)
{
DuLnode *tmp=L;
int i=0;
while(tmp!=NULL)
{
cout<<"第"<<i+1<<"个元素的数据:"<<tmp->data<<endl;
i++;
tmp=tmp->next;
}
}
//在不带头节点的单链表中,清空单链表就是销毁链表
//清空所有的元素
void CleanList(DuLinkList& L)
{
//DuLnode *tmp=L;
DuLnode *p=NULL;
/*while(tmp)
{
if(ListLength(tmp)==1)
{
cout<<"tttt"<<endl;
tmp=tmp->next;
delete L;
L=NULL;
}
else
{
p=tmp;
tmp=tmp->next;
delete p;
}
}
*/
while(L)
{
p=L;
L=L->next;
delete p;
}
L=NULL;
}
void DestoryList(DuLinkList& L)
{
}
//判断链表是否为空
//头节点和头指针还在(空表)
int isEmpty(DuLinkList L)
{
if(!L) return 1;//为空返回1
else return 0;
}
//按值查找 返回地址
//时间效率分析:查找次数与位置有关,O(n)
Lnode* LocationElem(DuLinkList L, int e)
{
DuLnode* p = L;
while (p && p->data != e)
{
p = p->next;
}
return p;
}
//按值查找,返回位置序号
int LocateElem_L(DuLinkList L, int e)
{
int j = 1;
DuLnode* p = L;
while (p && p->data != e)
{
p = p->next;
j++;
}
if (p) return j;
else return 0;
}
void menu()
{
cout<<"*******************************************"<<endl;
cout<<"1.构造一个带头结点的空链表 "<<endl;
cout<<"2.建立具有n的元素链表"<<endl;
cout<<"3.取第i个数据元素"<<endl;
cout<<"4.在链表中查找值为e的元素"<<endl;
cout<<"5.在第i位插入数据元素"<<endl;
cout<<"6.删除第i个数据元素"<<endl;
cout<<"7.求链表的表长"<<endl;
cout<<"8.判断链表是否为空"<<endl;
cout<<"9.清空链表"<<endl;
cout<<"10.遍历链表"<<endl;
cout<<"11.退出"<<endl;
cout<<"*******************************************"<<endl;
}
int main()
{
int choice;
DuLinkList L;
DuLnode *p=NULL;
int i2,e2,e,i1,e1;
int t,n,x1;
int x,data;
while(1)
{
menu();
cout<<"请输入你的选择"<<endl;
cin>>choice;
switch(choice)
{
case 1:
InitList(L);
cout<<"成功初始化"<<endl;
break;
case 2:
cout<<"请选择你想要创建链表的方法"<<endl;
cout<<"1.是头插法\t2.是尾插法"<<endl;
cin>>t;
if(t==1)
{
int a1[N];
cout<<"请输入需要多少个节点"<<endl;
cin>>n;
for(int i=0;i<n;i++)
{
cout<<"请输入第"<<i+1<<"个节点的数据"<<endl;
cin>>a1[i];
}
CreateList_F(L,a1,n);
}
else
{
int a2[N];
cout<<"请输入需要多少个节点"<<endl;
cin>>n;
for(int i=0;i<n;i++)
{
cout<<"请输入第"<<i+1<<"个节点的数据"<<endl;
cin>>a2[i];
}
CreateList_L(L,a2,n);
}
break;
case 3:
cout<<"输入要查找的位置"<<endl;
cin>>x;
p=GetElem(L,x);
if(p)
cout<<"第"<<x<<"个元素的值为:"<<p->data<<endl;
else
cout<<"输入的位置越界"<<endl;
break;
case 4:
cout<<"输入值"<<endl;
cin>>e;
x1=LocateElem_L(L,e);
cout<<"位置为:"<<x1<<endl;
break;
case 5:
cout<<"请输入要插入哪里跟节点的数据"<<endl;
cin>>i1>>e1;
LinkList_Dul(L,i1,e1);
break;
case 6:
cout<<"请输入要删除哪个节点"<<endl;
cin>>i2;
istDelte_Dul(L,i2,e2);
cout<<"删除成功,原来的节点的数据为"<<e2<<endl;
if(!L)
cout<<"全部节点删除为空"<<endl;
break;
case 7:
cout<<"长度为"<<ListLength(L)<<endl;
break;
case 8:
if(isEmpty(L)) cout<<"链表为空"<<endl;
else cout<<"链表不为空"<<endl;
break;
case 9:
CleanList(L);
cout<<"清空成功"<<endl;
break;
case 10:
ListTraverse(L);
break;
case 11:
cout<<"成功退出"<<endl;
exit(0);
default:
cout<<"请重新输入"<<endl;
break;
}
}
return 0;
}
五、测试结果
图一
图二
图三
图四
图五
图六
图七
图八
六、总结
在双向链表中有些操作(如:ListLength、GetElem等),因仅涉及单个方向的指针,故它们的算法与单链表的相同。但在插入、删除时,则需同时修改两个方向上的指针,与单链表有些区别,两者的操作的时间复杂度均为 O(n)。