ヘッド ノードのない双方向リンク リスト

目次

序文

1. 二重連結リストの定義

2. 二重連結リストの C++ 言語記述

3. 連結リストでの基本操作の実現

3.1 ヘッド ノードのない空の二重リンク リストの作成

3.2 双方向連結リストを作成するための先頭挿入方法

3.3 二重連結リストを成立させる末尾挿入法

3.3 二重連結リストの長さを求める

3.4 i 番目の要素の値を見つける 

 3.5 二重連結リストの挿入

3.6 二重連結リストの削除 

3.7 トラバーサル

 3.8 空の連結リスト

3.9 リンクされたリストが空かどうかを判断する

3.10 値による検索、リターンアドレス

3.11 値で検索して位置番号を返す

4. コードの実装 

 5. 試験結果

6. まとめ


序文

この記事は、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) です.

 

 

 

 

 

 

 

 

おすすめ

転載: blog.csdn.net/m0_53679998/article/details/129866306