双向链表的基本操作(头歌)

一、第1关:双向链表的插入操作

任务描述
本关任务:编写双向链表的插入操作函数。

相关知识
双链表中用两个指针表示结点间的逻辑关系:指向其前驱结点的指针域prior,指向其后继结点的指针域next。双向链表的结点结构如图所示:

双向链表的结点结构

双向链表结点类型定义如下:

typedef struct node
{ ElemType data; //数据域
struct node *prior,*next; //分别指向前驱结点和后继结点的指针
} DLinkNode,*DLinkList;
双向链表中数据类型ElemType可以多种多样,但是在编程实现算法时针对不同数据类型,每类数据元素的输入输出是有区别的,双向链表的基本操作算法要在计算机上执行,须针对ElemType类型数据编写输入、输出、比较等函数:

void input(ElemType &s);
void output(ElemType s);
int equals(ElemType a,ElemType b);
首先讨论如何进行双向链表的初始化操作。

双向链表的初始化操作
创建一个空的双链表,它只有一个头结点,由L指向它,该结点的next域和prior域均为空,data域未设定任何值。

void InitList(DLinkList &L)
{ L=(DLinkNode *)malloc(sizeof(DLinkNode));
//创建头结点L
L->prior=L->next=NULL;
}
在带头结点的双向链表中,通常头结点的数据域可以不存储任何信息,尾结点的next域置为NULL。 如下图所示是一个带头结点的双向链表:

双向链表的逻辑示意图如下:
双向链表逻辑示意图

查找双向链表中第i个数据元素值的操作
其算法思想与单链表的求表中第i个元素运算算法完全相同。

int GetElem(DLinkList L,int i,ElemType &e)
{
int j=0;
DLinkNode *p=L; //p指向头结点,计数器j置为0
if (i<=0) return 0; //参数i错误返回0
while (p!=NULL && j<i)
{
j++;
p=p->next;
}
if (p==NULL) return 0; //未找到返回0
else
{
e=p->data;
return 1; //找到后返回1
}
}
双向链表的插入操作
先在双链表中查找到第i-1个结点,若成功找到该结点(由p所指向),创建一个以x为值的新结点s,将s结点插入到p之后即可。

在双链表中p结点之后插入s结点的操作步骤如下:
① 将结点s的next域指向结点p的下一个结点:
s->next=p->next;
② 若p不是最后结点(若p是最后结点,只插入s作为尾结点),则将p之后结点的prior域指向s:
p->next->prior=s;
③ 将s的prior域指向p结点:
s->prior=p;
④ 将p的next域指向s:
p->next=s;

双向链表插入示意图如下:

双向链表插入示意图
在双链表中可以通过一个结点找到其前驱结点,所以插入操作也可以改为:
在双链表中找到第i个结点p,然后在p结点之前插入新结点。

与单链表上的插入操作不同的是,在双链表中插入必须同时修改两个方向上的指针。

双向链表的遍历操作
其算法思想与单链表的输出元素值运算算法完全相同。

void DispList(DLinkList L)
{
DLinkNode *p=L->next;
while (p!=NULL)
{
vi( p->data );
p=p->next;
}
printf("\n");
}
在执行遍历函数时,用函数指针vi来实现对output()函数的调用。

编程要求
根据提示,在右侧编辑器 Begin-End 区间补充代码,完成双向链表的初始化操作,插入操作及遍历操作三个子函数的定义,具体要求如下:

void InitList(DLinkList &L);//构造一个空的双向链表L
int ListInsert(DLinkList &L,int i,ElemType e) ;//在双向链表L中第i个位置之前插入新的数据元素
void ListTraverse(DLinkList L,void(*vi)(ElemType));// 依次调用函数vi()输出双向链表L的每个数据元素
测试说明
平台会对你编写的代码进行测试:

测试输入:
5
12 47 5 8 69
1
99
预期输出:
插入成功,插入后双向链表如下:
99 12 47 5 8 69

测试输入:
5
12 47 5 8 69
7
99
预期输出:
插入位置不合法,插入失败!

输入说明
第一行输入双向链表的数据元素的个数M;
第二行输入双向链表M个整数;
第三行输入要插入元素的位置;
第四行输入要插入的数据元素的值。

输出说明
第一行输出插入是否成功的提示信息;
如果插入成功,第二行输出插入元素后的双向链表所有元素;如果插入失败,则不输出第二行。

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
using namespace std;

/* 定义ElemType为int类型 */
typedef int ElemType;
void input(ElemType &s);
void output(ElemType s);
int equals(ElemType a,ElemType b);

/* 双向链表类型定义 */
typedef struct node
{  ElemType data;		//数据域
   struct node *prior,*next;	//分别指向前驱结点和后继结点的指针
} DLinkNode,*DLinkList;

void InitList(DLinkList &L);
int ListInsert(DLinkList &L,int i, ElemType e) ;
void ListTraverse(DLinkList L,void(*vi)(ElemType));

int main()               //main() function 
{	
     DLinkList A;
     ElemType e;
     InitList(A);
      int n,i;
     // cout<<"Please input the list number ";
     cin>>n;
     for(i=1;i<=n;i++)
        { 
		   cin>>e;
         ListInsert(A, i, e);
       }
	//cout<<"请输入插入的位置:"<<endl;
	cin>>i;
	//cout<<"请输入插入的值:"<<endl;
	input(e);
	if(  ListInsert(A,i,e) )
    {
      cout<<"插入成功,插入后双向链表如下:"<<endl;
      ListTraverse(A,output) ;
    }
    else
    	cout<<"插入位置不合法,插入失败!"<<endl;
    return  0;  
 }


/*****ElemType类型元素的基本操作*****/
void input(ElemType &s)
{
	cin>>s;
}
void output(ElemType s)
{
	cout<<s<<" ";
}
int equals(ElemType a,ElemType b)
{
	if(a==b)
		return  1;
	else
		return  0;
}

/*****双向链表的基本操作*****/
void InitList(DLinkList &L)
{ 	//构造一个空的双向链表L
	/********** Begin **********/ 
L=(DLinkNode *)malloc(sizeof(DLinkNode));  
            //创建头结点L
L->prior=L->next=NULL;
	
	/********** End **********/	
}
int ListInsert(DLinkList &L,int i, ElemType e) 
{
	// 在带头结点的双向链表L的第i个元素之前插入元素e  
	/********** Begin **********/ 
	DLinkList p,q;
    p=L;
    q=(DLinkList)malloc(sizeof(DLinkList));
    int j=0;
    while(p!=NULL && j<i-1)
    {
        
        j++;
        p=p->next;
    }
    if (p==NULL) 
    return 0;    //未找到返回0
    q->data=e;
    q->next=p->next;
    p->next=q;
    q->prior=p;
    return 1;
	/********** End **********/
}

void ListTraverse(DLinkList L,void(*vi)(ElemType))
{ 
	// 输出带头结点的双向链表L的每个元素 
	/********** Begin **********/ 
	DLinkList p=L->next;
    while (p!=NULL)
    { 
        vi( p->data );    
        p=p->next;
    }
    printf("\n");
    
	/********** End **********/
}

二、第2关:双向链表的删除操作

任务描述
本关任务:编写双向链表的删除操作函数。

相关知识
先在双向链表中查找到第i个结点,若成功找到该结点(由p所指向),通过前驱结点和后继结点的指针域改变来删除p结点。

在双链表中删除p结点(其前驱结点为pre)的操作:  
① 若p不是尾结点,则将其后继结点的prior域指向pre结点:
p->next->prior=pre
② 将pre结点的next域改为指向p结点的后继结点:
pre->next=p->next

双向链表删除示意图

在双向链表中删除也必须同时修改两个方向上的指针。

在双向链表中,那些只涉及后继指针的算法,如求表长度、取元素、元素定位等,与单链表中相应的算法相同,但对于插入和删除操作则涉及到前驱和后继两个方向的指针变化,因此与单链表中的算法不同。

编程要求
根据提示,在右侧编辑器 Begin-End 区间补充代码,完成双向链表的删除操作函数的定义,具体要求如下:

int ListDelete(DLinkList L,int i,ElemType &e);// 在双向链表L中删除第i个元素,并由e返回其值
测试说明
平台会对你编写的代码进行测试:

测试输入:
5
12 47 5 8 69
1
预期输出:
删除成功,删除后双向链表如下:
47 5 8 69
删除元素的值:12

测试输入:
5
12 47 5 8 69
6
预期输出:
删除位置不合法,删除失败!

输入说明
第一行输入双向链表的长度M;
第二行输入双向链表的M个整数;
第三行输入要删除元素的位置;

输出说明
第一行输出删除是否成功的提示信息;
如果删除成功,第二行输出删除元素后的双向链表;第三行输出删除的数据元素;如果删除位置不合法,不输出第二行和第三行。

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
using namespace std;

/* 定义ElemType为int类型 */
typedef int ElemType;
void input(ElemType &s);
void output(ElemType s);
int equals(ElemType a,ElemType b);

/* 双向链表类型定义 */
typedef struct node
{  ElemType data;		//数据域
   struct node *prior,*next;	//分别指向前驱结点和后继结点的指针
} DLinkNode,*DLinkList;

void InitList(DLinkList &L);
int ListInsert(DLinkList &L,int i, ElemType e) ;
int ListDelete(DLinkList L,int i,ElemType &e);
void ListTraverse(DLinkList L,void(*vi)(ElemType));

int main()               //main() function 
{	
	DLinkList A;
	ElemType e;
	InitList(A);
	int n,i;
	// cout<<"Please input the list number ";
	cin>>n;
	for(i=1;i<=n;i++)
	{ 
		cin>>e;
		ListInsert(A, i, e);
	}
	//ListTraverse(A,output) ;
	//cout<<"请输入删除的位置:"<<endl;
	cin>>i;
	if(  ListDelete(A,i,e) )
	{
		cout<<"删除成功,删除后双向链表如下:"<<endl;
		ListTraverse(A,output) ;
		cout<<"删除元素的值:";
	    output(e);
    cout<<endl;
	}
	else
		cout<<"删除位置不合法,删除失败!"<<endl;
}


/*****ElemType类型元素的基本操作*****/
void input(ElemType &s)
{
	cin>>s;
}
void output(ElemType s)
{
	cout<<s<<" ";
}
int equals(ElemType a,ElemType b)
{
	if(a==b)
		return  1;
	else
		return  0;
}

/*****双向链表的基本操作*****/
void InitList(DLinkList &L)
{ 	//构造一个空的双向链表L
	L=(DLinkList)malloc(sizeof(DLinkNode)); // 产生头结点,并使L指向此头结点
	L->next=NULL; // 指针域为空
	L->prior=NULL; 	
}

int ListInsert(DLinkList &L,int i, ElemType e) 
{
	// 在带头结点的双向链表L的第i个元素之前插入元素e  
	int j=0;
	DLinkNode *p=L,*s;
	if (i<=0) return 0;	      //参数i错误返回0
	while (p!=NULL && j<i-1)	   //查找第i-1个结点p
	{	
		j++;
		p=p->next;
	}
   if (p==NULL) 
		return 0;	   //未找到返回0
   else
   {	
        s=(DLinkNode *)malloc(sizeof(DLinkNode));
        s->data=e;		//创建一个存放元素x的新结点
        s->next=p->next;	//对应插入操作的步骤①
        if (p->next!=NULL)	//对应插入操作的步骤②
        p->next->prior=s;
        s->prior=p;		//对应插入操作的步骤③
        p->next=s;		//对应插入操作的步骤④
        return 1;		//插入运算成功,返回1
    }
	
}

void ListTraverse(DLinkList L,void(*vi)(ElemType))
{ 
	// 输出带头结点的双向链表L的每个元素 
	DLinkList p=L->next;
	while(p)
	{
		vi(p->data);
		p=p->next;
	}
	printf("\n");	
}

int  ListDelete(DLinkList L,int i,ElemType &e)    // 不改变L
{ 
	// 在带头结点的双向链表L中,删除第i个元素,并由e返回其值
	/********** Begin **********/ 
	DLinkList p,q;
    p=L;
    int n=0;
    while(p->next&&n<i-1)
    {
        n++;
        p=p->next;

    }
    if(!p->next||n>i-1)
    {
        return 0;
    }
    q=p->next;
    if(p->next->next)
    {
        p->next=p->next->next;
        p->next->next->prior=p;

    }
    else{
        p->next=NULL;
    }
    e=q->data;
    free(q);
    return e;
	/********** End **********/
}
  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ଲଇଉକ ଲ ̊ଳ

多谢大哥赏赐

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

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

打赏作者

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

抵扣说明:

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

余额充值