C语言 带头双向循环链表的基本操作

2024-06-03 12:12

本文主要是介绍C语言 带头双向循环链表的基本操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

带头双向循环链表的基本操作

  • 结构体定义
  • 初始化
  • 创建新节点
  • 头插
  • 头删
  • 尾插
  • 尾删
  • 查找
  • 在指定位置之后插入
  • 删除指定位置的值
  • 打印

结构体定义

typedef int DataType;
typedef struct LinkNode
{DataType data;struct LinkNode* prev;struct LinkNode* next;
}LNode;

初始化

有两种初始化方式
第一种你需要在main函数里或者全局定义一个LNode* phead,注意,只需要定义就可以了,然后再调用Init(&phead);

为什么要传地址呢?
因为在Init函数内部我们需要对phead进行操作(分配空间),那么一旦需要对参数进行操作,那就不能只穿本身,而要传地址。

void Init(LNode** pphead)
{*pphead=(LNode*)malloc(sizeof(LNode));(*pphead)->data=-1;(*pphead)->next=(*pphead)->prev=*pphead;
}

这一种的话,不需要在main函数里创建,直接调用Init_1()函数就行。

void Init_1()
{LNode* phead=(LNode*)malloc(sizeof(LNode));phead->data=-1;phead->prev=phead->next=phead;
}

初始化以后,头结点就是这样子了:
在这里插入图片描述

创建新节点

函数参数为DataType类型

LNode* CreateNode(DataType x)
{LNode* newnode=(LNode*)malloc(sizeof(LNode));newnode->prev=newnode->next=NULL;//这个地方也将prev和next初始化为newnode自身//因为这两个指针后续都会改变方向,因此初始状态无所谓。return newnode;
}

头插

这里的头插指的是在第一个有效节点之前插入,也就是插入到head的后一个。

我们第一步先把新节点的prev和next设置好,因为这样做不影响链表结构,肯定先把简单的搞了嘛~
在这里插入图片描述在这里插入图片描述然后再修改head->next和head->next->prev(图中1号节点)的指向,至于这两个谁先谁后,那就无所谓了。

在这里插入图片描述
头插完以后就是这个样子了

void PushHead(LNode* phead,DataType x)
{LNode* new=CreateNode(x);new->next=phead->next;new->prev=phead;phead->next->prev=new;phead->next=new;
}

头删

头删需要注意的一个点就是判断是否只剩下了一个节点,有些题目会要求头删直至为NULL,有的会要求删到只剩最后一个,我们这里以后者为例
在这里插入图片描述先这么改
在这里插入图片描述再这么改(这两步顺序可以变)
在这里插入图片描述最后再释放掉new节点就可以了(当然考试的时候不释放也行,平时养成这个习惯还是很好的!)

void DelHead(LNode* phead)
{if(phead->next==phead)return;else{LNode* tmp=phead->next;phead->next=tmp->next;tmp->next->prev=phead;   free(tmp);     }
}

尾插

尾插其实就是在头结点的前一个插,所以简单程度可想而知:

void PushBack(LNode*phead,DataType x)
{LNode*new=CreateNode(x);new->next=phead;new->prev=phead->prev;phead->prev->next=new;phead->prev=new;
}

尾删

跟刚才尾插一个道理,就是头结点的前一个。

void DelBack(LNode*phead)
{LNode* tmp=phead->prev;phead->prev=tmp->prev;tmp->prev->next=phead;free(tmp);
}

查找

查找的时候,有时候对查找的开始位置可能也有限制。如果是带头的双循环链表比较好办,如果是不带头的,如果要从phead开始查找,那么开始访问位置cur就得是phead->prev;
我们这里介绍从phead的next开始访问的情况,其他情况趋同,只需要改变初始访问指针cur的值就可以

LNode* Find(LNode* phead,DataType x)
{LNode* cur=phead->next;while(cur!=phead){if(cur->data==x)return cur;cur=cur->next;}return NULL;
}

在指定位置之后插入

跟头插简直一模一样,就是把参数换了个名儿

void Insert(LNode* pos,DataType x)
{LNode* new=CreateNode(x);new->next=pos;new->prev=pos->prev;pos->prev->next=new;pos->prev=new;
}

删除指定位置的值

跟刚才头删尾删一样,有些题可能需要删到最后一个为止

void DelPos(LNode* pos)
{if(pos->next=pos)return;else{LNode* front=pos->prev;LNode* after=pos->next;front->next=after;after->prev=front;}
}

打印

这个地方比较巧妙,如果想让phead的值第一个被打印,那么循环就要用do while 因为如果用了while(cur!=phead),那么循环完全进不去,因为访问指针cur的初始值就为phead,用do while可以先执行一次,执行完之后cur的指向就不再是phead了,然后再判断循环条件。

void Print(LNode* phead)
{LNode* cur=phead;do{printf("%d ",cur->data);cur=cur->next;} while (cur!=phead);}

有两道非常好的双向循环链表的题推荐给大家:
link
这是我写的我们学校作业的题解,
空闲空间申请模拟和 ***文件加密!***这两道题都用到了循环链表,但是比刚刚的基础操作要复杂很多,但是基本的思想都是大差不差的,我在本文中提到的一些注意事项,在这两道题中都有体现,比如说开始访问位置,判断是否为空等等,大家如果想要提高一下可以去看看那两道题哈~

这篇关于C语言 带头双向循环链表的基本操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1026956

相关文章

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

Go语言中json操作的实现

《Go语言中json操作的实现》本文主要介绍了Go语言中的json操作的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录 一、jsOChina编程N 与 Go 类型对应关系️ 二、基本操作:编码与解码 三、结构体标签(Struc

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

python语言中的常用容器(集合)示例详解

《python语言中的常用容器(集合)示例详解》Python集合是一种无序且不重复的数据容器,它可以存储任意类型的对象,包括数字、字符串、元组等,下面:本文主要介绍python语言中常用容器(集合... 目录1.核心内置容器1. 列表2. 元组3. 集合4. 冻结集合5. 字典2.collections模块

基于Go语言开发一个 IP 归属地查询接口工具

《基于Go语言开发一个IP归属地查询接口工具》在日常开发中,IP地址归属地查询是一个常见需求,本文将带大家使用Go语言快速开发一个IP归属地查询接口服务,有需要的小伙伴可以了解下... 目录功能目标技术栈项目结构核心代码(main.go)使用方法扩展功能总结在日常开发中,IP 地址归属地查询是一个常见需求:

Spring 依赖注入与循环依赖总结

《Spring依赖注入与循环依赖总结》这篇文章给大家介绍Spring依赖注入与循环依赖总结篇,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Spring 三级缓存解决循环依赖1. 创建UserService原始对象2. 将原始对象包装成工

GO语言短变量声明的实现示例

《GO语言短变量声明的实现示例》在Go语言中,短变量声明是一种简洁的变量声明方式,使用:=运算符,可以自动推断变量类型,下面就来具体介绍一下如何使用,感兴趣的可以了解一下... 目录基本语法功能特点与var的区别适用场景注意事项基本语法variableName := value功能特点1、自动类型推

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Go语言连接MySQL数据库执行基本的增删改查

《Go语言连接MySQL数据库执行基本的增删改查》在后端开发中,MySQL是最常用的关系型数据库之一,本文主要为大家详细介绍了如何使用Go连接MySQL数据库并执行基本的增删改查吧... 目录Go语言连接mysql数据库准备工作安装 MySQL 驱动代码实现运行结果注意事项Go语言执行基本的增删改查准备工作

Go语言使用Gin处理路由参数和查询参数

《Go语言使用Gin处理路由参数和查询参数》在WebAPI开发中,处理路由参数(PathParameter)和查询参数(QueryParameter)是非常常见的需求,下面我们就来看看Go语言... 目录一、路由参数 vs 查询参数二、Gin 获取路由参数和查询参数三、示例代码四、运行与测试1. 测试编程路