【头歌】双向链表的基本操作

news2024/10/7 16:21:55

双向链表的基本操作

第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 **********/ 
    DLinkNode *p=L,*s;
    int j=0;
    if (i<=0) return 0;	
    while(p!=NULL&&j<i-1)
    {
        p=p->next;
        j++;
    }
    if (p==NULL) 
		return 0;	
    else{
          s=( DLinkNode *)malloc(sizeof(DLinkNode));
          s->data=e;
          s->next=p->next;
          if(p->next!=NULL)
          {
              p->next->prior=s;
              s->prior=p;
              p->next=s;
          }
          else
          {
              
              s->prior=p;
              p->next=s;
              s->next=NULL;
          }
          return 1;
    }

	
	/********** End **********/
}

void ListTraverse(DLinkList L,void(*vi)(ElemType))
{ 
	// 输出带头结点的双向链表L的每个元素 
	/********** Begin **********/ 
  DLinkNode *p=L->next;
    while(p)
    {
        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);
	}

	//cout<<"请输入删除的位置:"<<endl;
	cin>>i;
	if(  ListDelete(A,i,e) )
	{
		cout<<"删除成功,删除后双向链表如下:"<<endl;
		ListTraverse(A,output) ;
		cout<<"删除元素的值:"<<e<<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 **********/ 
    int j=0;
	DLinkNode *p=L;
	if (i<=0) return 0;	      //参数i错误返回0
	while (p!=NULL && j<i)	   //查找第i个结点p
	{	
		j++;
		p=p->next;
       
	}
    if(p==NULL)return 0;
    DLinkNode *pre=p->prior;
    e=p->data;
   if (p->next)
   {
       
       p->next->prior=pre;
       pre->next=p->next;
       free(p);
       return 1;		
       
   }		
   else
   {  
      
       pre->next=NULL;
       free(p);
       return 1;
    }
	
	
	/********** End **********/
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/179290.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL数据库面试题[万字汇总]

1) MySQL数据库相关错题本1、存储引擎相关1、MySql的存储引擎的不同MySQL存储引擎主要有InnoDB, MyISAM, Memory, 这三个区别在于:Memory是内存数据引擎, 会断电重启(在双M或者主从架构下会产生较多异常), 且不支持行级锁. 默认索引是数组索引, 支持B索引InnoDB和MyISAM的区别:…

流批一体计算引擎-5-[Flink]的Python Table API和SQL程序

参考Flink从入门到入土&#xff08;详细教程&#xff09; 参考flink的默认窗口触发机制 参考彻底搞清Flink中的Window 参考官方Python API文档 1 IDEA中运行Flink 从Flink 1.11版本开始, PyFlink 作业支持在 Windows 系统上运行&#xff0c;因此您也可以在 Windows 上开发和…

【数据结构】极致详解:树与二叉树(上)——结构与概念

目录 &#x1f6eb;前言&#x1f6eb;&#xff1a; &#x1f680;一、树&#x1f680;&#xff1a; 1.树的概念&#xff1a; 2.树的相关概念&#xff1a; 3.树的表示&#xff1a; 4.树的实际使用场景&#xff1a; &#x1f6f0;️二、二叉树&#x1f6f0;️&#xff1a;…

acwing-Diango项目 (后半)

acwing-Django项目 文章目录acwing-Django项目前言5. 创建账号系统5.1用户名密码登录写登录界面写注册界面写动作 实现三个函数 register login logout5.2 Web端acapp一键登录在django中集成redis(准备工作)首先 pip install django_redis配置一下缓存启动redis-serverredis在d…

特征工程——文本特征

文本特征 expansion编码 consolidation编码 文本长度特征 标点符号特征 词汇属性特征 特殊词汇特征 词频特征 TF-IDF特征 LDA特征 下面的文章主要是梯度提升树模型展开的&#xff0c;抽取的特征主要为帮助梯度提升树模型挖掘其挖掘不到的信息&#xff0c;本文介绍的所…

NodeJS Web 框架 Express 之中间件

NodeJS Web 框架 Express 之中间件参考描述中间件next()一个简单的中间件函数使用全局中间件局部中间件共享注意事项位置next()分类错误级中间件内置中间件express.urlencoded()express.json()第三方中间件参考 项目描述哔哩哔哩黑马程序员搜索引擎Bing 描述 项目描述Edge109…

从0-1开始 测试ZLMediaKit推拉流性能、延时性能

流媒体开发系列文章 文章目录流媒体开发系列文章前言一、环境准备&#xff1f;二、拉流测试过程三、推流测试过程三、延时测试总结前言 目前、比较有名的流媒体服务器有ZLMediaKit、srs、live555、eadydarwin等。因为srs是单线程服务、对于多核服务器的支持需要通过部署多个服…

pytorch深度学习基础(十一)——常用结构化CNN模型构建

结构化CNN模型构建与测试前言GoogLeNet结构Inception块模型构建resNet18模型结构残差块模型构建denseNet模型结构DenseBlocktransition_block模型构建结尾前言 在本专栏的上一篇博客中我们介绍了常用的线性模型&#xff0c;在本文中我们将介绍GoogleNet、resNet、denseNet这类…

APT之木马静态免杀

前言 这篇文章主要是记录手动编写代码进行木马免杀&#xff0c;使用工具也可以免杀&#xff0c;只不过太脚本小子了&#xff0c;而且工具的特征也容易被杀软抓到&#xff0c;指不定哪天就用不了了&#xff0c;所以要学一下手动去免杀木马&#xff0c;也方便以后开发一个只属于…

blender导入骨骼动画方法[psa动作]

先导入女性的psk文件 然后调整缩放大小和人物一样,包括角度朝向. ctrla应用所有改变 然后选择psk文件以及其他人物模型的全部 ,然后 在Layout-物体-父级 -附带空顶相点组 image.png之后会发现所有人物多了修改器,点击其中一个修改器 点添加修改器 -数据传递 勾选顶点数据-选择顶…

人员动作行为AI分析系统 yolov5

人员动作行为AI分析系统通过pythonyolo系列网络学习模型&#xff0c;对现场画面人员行为进行实时分析监测&#xff0c;自动识别出人的各种异常行为动作&#xff0c;立即抓拍存档预警同步回传给后台。 我们使用YOLO算法进行对象检测。YOLO是一个聪明的卷积神经网络(CNN)&#xf…

带滤波器的PID控制仿真-1

采用低通滤波器可有效地滤掉噪声信号&#xff0c;在控制系统的设计中是一种常用的方法。基于低通滤波器的信号处理实例设低通滤波器为&#xff1a;采样时间为1ms&#xff0c;输入信号为带有高频正弦噪声&#xff08; 100Hz&#xff09;的低频&#xff08;0.2Hz)正弦信号。采用低…

离散数学与组合数学-05树

文章目录离散数学与组合数学-05树5.1 认识树5.1.1 树的模型5.1.2 树的应用5.2 无向树5.2.1 定义5.2.2 树的性质5.2.3 性质应用5.3 生成树5.3.1 引入5.3.2 定义5.3.3 算法5.3.4 应用5.4 最小生成树5.4.1 引入5.4.2 定义5.4.3 算法5.5 根树5.5.1 根数定义5.5.2 倒置法5.5.3 树的家…

【编程入门】开源记事本(SwiftUI版)

背景 前面已输出多个系列&#xff1a; 《十余种编程语言做个计算器》 《十余种编程语言写2048小游戏》 《17种编程语言10种排序算法》 《十余种编程语言写博客系统》 《十余种编程语言写云笔记》 本系列对比云笔记&#xff0c;将更为简化&#xff0c;去掉了网络调用&#xff0…

C++模板进阶

这篇文章是对模板初阶的一些补充&#xff0c;让大家在进行深一层的理解。 文章目录1. 非类型模板参数2. 模板的特化2.1 概念2.2 函数模板特化2.3 类模板特化2.3.1 全特化2.3.2 偏特化2.4 类模板特化应用示例3 模板分离编译3.1 什么是分离编译3.2 模板的分离编译3.3 解决方法4.…

【各种**问题系列】什么是 LTS 长期支持

目录 &#x1f341; 什么是长期支持&#xff08;LTS&#xff09;版本&#xff1f; &#x1f342; LTS 版本的优点&#xff1a; &#x1f341; 什么是 Ubuntu LTS&#xff1f; &#x1f342; Ubuntu LTS 软件更新包括什么&#xff1f; 在 Linux 的世界里&#xff0c;特别是谈…

【Java开发】Spring Cloud 08 :链路追踪

任何一个架构难免会出现bug&#xff0c;微服务相比于单体架构日志查询更为困难&#xff0c;因此spring cloud推出了Sleuth等组件的链路追踪技术来实现报错信息的定位及查询。项目源码&#xff1a;尹煜 / coupon-yinyu GitCode1 调用链追踪我们可以想象这样一个场景&#xff0c…

单一数字评估指标、迁移学习、多任务学习、端到端的深度学习

目录1.单一数字评估指标(a single number evaluation metric)有时候要比较那个分类器更好&#xff0c;或者哪个模型更好&#xff0c;有很多指标&#xff0c;很难抉择&#xff0c;这个时候就需要设置一个单一数字评估指标。例1&#xff1a;比较A&#xff0c;B两个分类器的性能&a…

Android MVVM的实现

Android MVVM的实现 前言&#xff1a; 在我们写一些项目的时候&#xff0c;通常会对一些常用的一些常用功能进行抽象封装&#xff0c;简单例子&#xff1a;比如BaseActivity&#xff0c;BaseFragment等等…一般这些Base会去承载一些比如标题栏&#xff0c;主题之类的工作&…

提权漏洞和域渗透历史漏洞整理

Windows提权在线辅助工具 https://i.hacking8.com/tiquan/&#x1f334;Kernel privilege escalation vulnerability collection, with compilation environment, demo GIF map, vulnerability details, executable file (提权漏洞合集) https://github.com/Ascotbe/Kernelhu…