3.双链表和循环链表以及相关代码

3.双链表和循环链表

双链表是基于对单链表的优化(由于单链表没有前驱指针,所以要找到前面一个结点不好找,只能去遍历,因此有了双链表)

怎么实现往前找呢?--->加一个指针域

双向链表 就是在单链表的基础上 每个结点增加一个指针域,该指针域保存上一个结点的地址

相关概念:与单链表一样

操作:增删改查

  1. 初始化一个带头结点的空的双链表
  2. 增:
  1. 头插法:固定在头结点后插入一个数据(结点),新插入的结点做首元结点
  2. 尾插:固定在链表最后插入一个数据(节点),新插入的节点做尾结点
  3. 中间插:在中间指定数据后插入一个数据(得先查找)
  1. 改+查:查在中间插中已有;改则在查后加入一个改数据域代码(p->data=k)即可
  2. 删除:删除指定的数据(结点)
  1. 判断链表是否为空 如果头结点后面没有首元结点就为空,
  2. 查找k所在结点
  3. 删除操作

循环链表:

单链表:头结点是l 尾结点是r(循环遍历找尾结点)

循环单链表:把单链表首尾相接:r->next=l

循环单链表的结点结构和单链表的结点结构一样

循环单链表的操作和单链表的操作一样,唯一的区别就是操作中的r->next=NULL 改为r->next=l;

双链表:头结点是l 尾结点是r(l->pre)

循环双链表:把双链表相接:l->pre=r  r->next=l

循环双链表的结点结构和双链表的结点结构一样

循环双链表的操作和双链表的操作一样,唯一的区别就是操作中的r->next=NULL 改为r->next=l;

l->pre=NULL 改为 l->pre=r;

线性表:顺序表+链表

///

假设对线性表有4中操作:

  1. 删除第一个数据 ---头指/尾指针的循环单链表
  2. 删除最后一个数据 ---尾指针/头指针循环双链表
  3. 在第一个数据之前插入一个新数据
  4. 在最后一个数据之后插入一个新数据

现在用链式结构实现该线性表,以下哪个链式结构比较好:C

A 只有尾指针 没有头指针的循环单链表

B 只有尾指针 没有头指针的飞循环单链表

C 只有头指针 没有尾指针的循环双链表

D 既有头指针 又有尾指针的循环单链表

一个链表最常用的操作是末尾插入和删除,哪个链表最省时间A

A带头结点的循环双链表

B循环单链表

C带尾指针的循环单链表

D单链表

#include<stdio.h>
#include<stdlib.h>

typedef struct Node{
	int data;//数据域 
	struct Node* next;//指针:保存下一个结点的地址 
	struct Node* pre; //指针:保存上一个结点的地址 
}Node,*linklist;
//初始化一个带头结点的空双链表 
linklist initlist()
{
	linklist l=(Node *)malloc(sizeof(Node));	//声明了一个头指针l并分配了空间 
	if(l==NULL)
	{
		printf("头结点初始化,空间分配失败\n");
	}
	else
	{
		l->next=NULL;
		l->pre=NULL;
	}
	return l;
}
//头插法 插入数据k 
linklist head_insert(linklist l,int k)
{
	Node* s=(Node*)malloc(sizeof(Node));
	if(s==NULL)
	{
		printf("头插时,空间分配失败");
		return l;
	}
	s->data=k;
	//顺序可以不同(但要保证不能使插入的下一个数据丢失就行) 
	s->next=l->next;//1
	if(l->next!=NULL)//看首元结点存不存存--->第一次对链表进行插入就不存在 
	{
		l->next->pre=s;//2
	}
	l->next=s;//3
	s->pre=l; //4
	return l; 
}
//尾插法 
linklist tail_insert(linklist l,int k)
{
	//利用循环遍历找到最后一个结点  
	Node *p=l;//声明一个指针p当作链表来辅助遍历 
	while(p->next!=NULL)
	{
		p=p->next;
	}
	Node *s=(Node*)malloc(sizeof(Node));
	if(s==NULL)
	{
		printf("尾插时,空间分配失败\n");
		return l;
	}
	s->data=k;
//	//
	p->next=s;
	s->pre=p;
	s->next=NULL;
	//可以这样的顺序吗 ---->可以 说明这里可以随便可以顺序不一样 
//	s->next=NULL;
//	s->pre=p;
//	p->next=s;
	return l;
}
//中间插入涉及查找---->查找涉及链表整体不为空即p!=NULL 插入(尾插法)涉及结点不为空即p->next!=NULL 
linklist mid_insert(linklist l,int x,int k)
{
	//查找 
	Node* p=l->next;//先让p=首元结点 遍历链表找到其对应数据域 
	while(p!=NULL&&p->data!=x)
	{
		p=p->next;
	}
	if(p==NULL)
	{
		printf("x不存在,无法插入\n");
		return l;
	}
	//插入 
	Node *s=(Node*)malloc(sizeof(Node));
	if(s==NULL)
	{
		printf("中间插入时,空间分配失败\n");
		return l;
	}
	s->data=k;
	s->next=p->next;//1
	if(p->next!=NULL)//要把结点s赋值的需要判断 
	{
		p->next->pre=s;//2
	}
	p->next=s;//3
	s->pre=p;//4
	
	return l;
 } 
void show(linklist l)
{
	Node* p = l->next;//从首元结点开始
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");

}
linklist delet(linklist l,int k)
{
	//判断链表是否为空 如果头结点后面没有首元结点就为空 
	 if(l->next==NULL)
	 {
	 	printf("空链表,无法删除\n");
	 	return l;
	 }
	 //查找k所在结点
	  Node *p=l->next;
	  while(p!=NULL&&p->data!=k)
	  {
	  	p=p->next;
	  }
	  if(p==NULL)
	  {
	  	printf("%d不存在,无法删除\n",k);
	  	return l;
	  }
	  //删除操作 
	  Node *s=p->pre;//把前一个地址赋值给s结点 
	  s->next=p->next;//1
	  //如果p后面没节点了(p是最后一个结点), p->next就不存在,所以就不用 p->next->pre去指向s 
	  if(p->next!=NULL) 
	  {
	  	p->next->pre=s;//2
	  }
		free(p);
		p=NULL;
		return l;
	  
}
int main()
{
	linklist l=initlist();
	//头插 9 7 6
	l=head_insert(l,6); 
	l=head_insert(l,7); 
	l=head_insert(l,9); 
	show(l);
	//尾插 9 7 6 10 9 7
	l=tail_insert(l,10);
	l=tail_insert(l,9);
	l=tail_insert(l,7);		
	show(l); 
	//中间插入 9 7 8 6 10 9 7
	l=mid_insert(l,7,8);
	show(l); 
	//删除 9 7 8 10
	l=delet(l,6);
	show(l);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值