不带头节点的双向链表

目录

前言

一、双向链表的定义

二、双向链表的 C++ 语言描述

三、链表中基本操作的实现

3.1构造一个不带头结点的空双向链表

3.2头插法建立双向链表

3.3尾插法建立双向链表

3.3求双向链表长度

3.4求第i个元素的值 

 3.5双向链表的插入

扫描二维码关注公众号,回复: 14814027 查看本文章

3.6双向链表的删除 

3.7遍历

 3.8清空链表

3.9判断链表是否为空

3.10按值查找,返回地址

3.11按值查找,返回位置序号

四、代码实现 

 五、测试结果

六、总结


前言

本文参考王卓老师的数据结构视频和严蔚敏老师的《数据结构》

一、双向链表的定义

用一组地址任意的存储单元存放线性表中的数据元素。

结点 = 数据元素+指针(指后继元素存储位置)

所谓双向链表,即有两个指针,分别指向前驱和后继元素

二、双向链表的 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)。

猜你喜欢

转载自blog.csdn.net/m0_53679998/article/details/129866306