数据结构--线性表(2)--链式存储(2)--双链表,循环链表,静态链表

1.双链表定义线性表

1).双链表定义线性表的思想

在单链表的基础上加一个指向前驱的指针

2).双链表定义线性表的结构体

typedef struct LNode{
int data; //数据域
struct LNode*prior;  //定义指向前驱节点的指针
struct LNode*next;  //定义指向后继节点的指针
}LNode;

3).双链表定义线性表的基本操作

双链表的建立

头插法

DLinkList Header_insert(DLinkList &L)//带头节点 
{
	DNode *p,*q,*s; int x;
	L = (DLinkList)malloc(sizeof(DNode));  //创建头节点 
	L->next = NULL;   //指针域为空 
	L->prior = NULL;
	q = L;
	printf("输入插入值:\n"); //输入要插入的数据 
	scanf("%d",&x);  //若输入9999则创建空表 
	while(x != 9999)
	{
		p = (DNode *)malloc(sizeof(DNode));//创建一个新节点 
		p->data = x; //	把元素放进数据域中 
		p->next = q->next; //指针域指向头节点指向的数据 
		p->prior = q;     //指头节点
		if(q->next != NULL) //当插入超过一个节点后要对头指指向下一个节点进行操作 
		  q->next->prior = p; 
		q->next = p;    //把头节点的指针指向新节点地址         
		printf("输入插入值(输入9999表示结束插入):\n");
		scanf("%d",&x);
	}
	printf("带头节点,使用头插法建立链表结束!\n"); 
	return L;
 } 

尾插法

DLinkList Tail_insert(DLinkList &L)
  {
  	DNode *p,*r; //r为尾指针 
    int x;
	L = (DLinkList)malloc(sizeof(DNode)); //创建一个新节点
	L->next = NULL;                       //指针域为空
	L->prior = NULL;
	r = L;                                //尾指针指向头节点 
	printf("输入插入值:\n");
	scanf("%d",&x);           
	while(x != 9999)   
   {
	p = (DNode *)malloc(sizeof(DNode));//创建一个新节点
	p->data = x;                       //把数据装入新节点数据域
	p->prior = r;                      //指向前驱节点 
	r->next = p;                       //尾指针指针域指向新节点 
	r = p;                             //尾指针指向新节点(保持尾指针一直在最后一个节点) 
	printf("输入插入值(输入9999表示结束插入):\n");
    scanf("%d",&x);
   }
   r->next = NULL;                     //将尾指针指向节点中指针域置空 
   printf("带头节点,使用尾插法建立链表结束!\n"); 
   return L;
  }

双链表在按值查找和按位查找和单链表一致。
由于双链表加入了一个指向前驱节点的指针,插入和删除操作和单链表操作大有不同。下面会具体说明双链表插入和删除操作。


删除操作

删除第 i 个节点思想:

  • 获取位序为 i 的节点。(前提 i 值合法)
  • 对被删除节点的前驱节点和后继节点进行操作。
  • 被删除节点的前驱节点的指向下一个节点的指针指向被删除节点的后继结点。
  • 被删除节点的后继结点指向上一个节点的指针指向被删除节点的前驱节点。

以下三种方式实现

void Delete_i(DLinkList &L,int i)//(带头节点)
  {//删除第i个节点
  	DNode *s,*pro,*late;
  	s = GetElem(L,i); //调用按序列查找函数,找到第i个节点并使p指向该节点 
	pro = s->prior;   //使pro指针指向被删除节点的前驱节点 
	pro->next = s->next;  //被删除节点的前驱节点指向被删除节点的后继节点 
	if(s->next != NULL)
	{
	late = s->next;   //使late指针指向被删除的后继节点  
	late->prior = pro;  //被删除节点的后继结点指向被删除节点的前驱结点
	} 
	free(s);          //释放节点的存储空间 
   } 

void Delete_i(DLinkList &L,int i)//(带头节点)
  {//删除第i个节点
  	DNode *s,*pro;
  	s = GetElem(L,i); //调用按序列查找函数,找到第i个节点并使p指向该节点 
	pro = s->prior;   //使pro指针指向被删除节点的前驱节点 
	pro->next = s->next;  //被删除节点的前驱节点指向被删除节点的后继节点 
	if(s->next != NULL)
	s->next->prior = pro; //被删除节点的后继结点指向被删除节点的前驱结点
	free(s);          //释放节点的存储空间 
   } 

下面代码更简洁

 void Delete_i(DLinkList &L,int i)//(带头节点)
{
	DNode *s;
  s = GetElem(L,i); //调用按序列查找函数,找到第i个节点并使p指向该节点
  s->prior->next = s->next; 
  if(s->next != NULL)
  s->next->prior = s->prior;
	free(s);          //释放节点的存储空间 
  } 

删除给点给值所在节点的思想:

  • 获取该值所在节点。(前提链表中存在该值)
  • 对被删除节点的前驱节点和后继节点进行操作。
  • 被删除节点的前驱节点的指向下一个节点的指针指向被删除节点的后继结点。
  • 被删除节点的后继结点指向上一个节点的指针指向被删除节点的前驱节点。

下面就只写一种方式,和按位序删除操作一致。

void Delete_P(DLinkList &L,ElemType e)//(带头节点)
  {
  	DNode *s,*pro,*late;
  	s = GetElem_P(L,e); //调用按序列查找函数,找到第i个节点并使p指向该节点 
	pro = s->prior;     //指向被删除节点的前驱节点
	pro->next = s->next; //前驱节点指向被删除节点的指针域所指向的地址 
	if(s->next != NULL)  //判断被删除节点的下一个节点是否为空 
     { 
	late = s->next;      //让指针指向别删除节点的后继结点 
	late->prior = pro;   //修改后该节点指向前驱节点指针指向的被删除节点的前驱节点 
	 } 
	free(s);          //释放节点的存储空间 
   } 
插入操作

在指定节点前插入新节点:

bool insert_pro(DLinkList &L,ElemType a,ElemType e)//前插 (在某节点位置前面插入) 
  {
  	DNode *p,*q;
  	int temp;
  	q = (DLinkList)malloc(sizeof(DNode));    //创建一个新节点
  	q->data = e;                         //把要插入的元素放入新节点中
  	p = GetElem_P(L,a);       //获取被删除节点
	if(p != NULL)             //若节点存在才进行插入
	{
		p->prior->next = q; //节点的前驱节点指向插入节点
		q->next = p;       //插入节点指向指定的节点,让其成为插入节点的后继结点
		p->prior = q;     //指定的节点指向插入节点,让插入节点成其前驱节点
	}
	else
	return 0;//不存在节点,返回0,插入失败
	return 1;//返回1插入成功
   } 

在指定节点后插入新节点:

bool insert_later(DLinkList &L,ElemType a,ElemType e)//后插(在某节点位置后面插入)
  {
  	DNode *p,*q;
  	int temp;
  	q = (DLinkList)malloc(sizeof(DNode));//创建一个新节点
  	q->data = e;                 //把要插入的元素放入新节点中
  	p = GetElem_P(L,a);
  	if(p != NULL)
  	{
  		q->next = p->next;
  		q->prior = p;
  		p->next = q;
	  }
	  else
  	    return 0;
    return 1;
  }

第i个位置插入

bool insert_location(DLinkList &L,int i,ElemType e)//在第i个位置插入元素 
  {
  	DNode *p,*q;
  	q = (DLinkList)malloc(sizeof(DNode));//创建一个新节点
  	q->data = e;                 //把要插入的元素放入新节点中 
 	p = GetElem(L,i-1);         //获取第i-1个位置节点 ,并使指针指向该节点 
 	if(p == NULL)               //i值不合法返回空,当i值不和法返回0 
 	return 0;
  	  q->next = p->next;
  	  q->prior = p;
  	  p->next = q;
  	return 1;
  } 

3.循环链表

1).循环单链表

循环单链表只是在单链表的最后一个节点指针域不为null,而是让该末尾指针指向头。
循环单链表中,由于表尾节点指针域指向头,不存在空,所以判空条件为是否等于头节点。
循环单链表插入,删除操作和单链表几乎一致。
单链表是一个环,所以在插入,删除操作中不用判断是否是表尾。
单链表遍历只能从头开始遍历,循环单链表可以从任意位置遍历链表。


4.静态链表

1).静态链表定义线性表的思想

静态链表借助数组来描述线性表的链式存储结构。
节点也有数据域和指针域,不同的是,它的指针域是节点的相对位置(数据下标)。
静态链表也要先分配一段连续的空间。

2).静态链表定义线性表的结构体

#define MaxSize  50
typedef struct {
int data; //数据域
int next;//下一个元素的数组下标
}SLinkList[MaxSize];

插入删除的基本操作与动态链表相同。

脑图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

An安之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值