目次
序文
この記事は、Wang Zhuo 氏のデータ構造ビデオと Yan Weimin 氏の「データ構造」を参照しています。
1. 二重連結リストの定義
任意のアドレスを持つストレージ ユニットのグループを使用して、線形テーブルにデータ要素を格納します。
ノード=データ要素+ポインタ(後続要素の格納場所を参照)
いわゆる二重連結リストには、先行要素と後続要素をそれぞれ指す 2 つのポインターがあります。
2. 二重連結リストの C++ 言語記述
コードは次のとおりです (例)。
typedef struct Lnode { int データ; struct Lnode* before, * next;// }DuLnode,*DuLinkList;
3. 連結リストでの基本操作の実現
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 の長さを使用して計算し、ループ内で 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 リンクされたリストが空かどうかを判断する
null の場合は 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;
}
4. コードの実装
#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;
}
5. 試験結果
図1
図Ⅱ
図 3
図 4
図 5
図 6
図 7
フィギュアエイト
6. まとめ
二重連結リストの一部の操作 (ListLength、GetElem など) は、単一方向のポインターのみを使用するため、それらのアルゴリズムは単方向連結リストの場合と同じです。ただし、挿入または削除する場合は、双方向のポインタを同時に変更する必要があり、これは単一リンク リストとは多少異なります. 両方の操作の時間計算量は O(n) です.