单项循环链表及其带头指针的链表

对于链表我们要仔细深入的学习它,为何呢,因为他是我们在后面学习非线性数据结构的基础,像后面的树,图等结构都是由链表演变出来的,所以我们这篇博客继续探究链表

带头指针的链表

我们上篇博客讲述了带头节点的链表

如图

在这里插入图片描述

然后演示出了一系列公式化的打法

像什么插入删除

//找到前置节点p
//插入
new_node->next=p->next;
p->next=new_node;
//删除
temp=p->next;
p->next=temp->next;
free(temp);

但是我们今天的主角带头指针的链表,你需要考虑的就多了,公式化的打法已经无法满足我们了

看图
在这里插入图片描述

那我们是否能复刻之前的打法,一套代码打天下?

肯定不行,你会发现你想要采用头插法一下就要如下

new_node->next=head;
head=new_node;

当你遇见空的头指针的时候还要if一下,看起来就麻烦。

那么有简化一下书写难度的方法吗?

有的兄弟,有的。

我们可引入一个dummy节点,这个节点是一个临时节点,然后将指针的信息临时传入进去,而后我们就可以继续我们的公式化打法处理了,处理完以后再将地址的 值返回。

如图
在这里插入图片描述

这样的好处就又回到了之前的公式化的打法,只要我们注意最后的维护就行了

具体的代码如下

带头指针的链表(代码)

结构定义及其接口


#ifndef CHAINLIST_H
#define CHAIN_LIST_H
//带头指针的单向链表,维护节点也是node
#include"common.h"//定义链表头结构只保存头指针
typedef struct {node_t *header;int cout;}ChainList_t;
//表头放到数据区,放到全局变量
//初始化
void initChainList(ChainList_t *table);
//释放空间
void destoryChainList(ChainList_t *table);//插入
int insertChainListHeader(ChainList_t* table, Element_t data);//头插法
int insertChainListpos(ChainList_t* table,int pos, Element_t data);//按位置插入//删除
int deleteChainListHeader(ChainList_t* table, Element_t data);//打印
void showChainList(const ChainList_t* table);#endif //CHAIN_LIST_H

对操作的具体实现


#include <stdio.h>
#include <stdlib.h>
#include "chainList.h"void initChainList(ChainList_t *table) {table->cout=0;table->header=NULL;
}int insertChainListHeader(ChainList_t *table, Element_t data) {node_t dummy;//定义临时节点dummy.next=table->header;node_t*p=&dummy;node_t* newnode=(node_t*)malloc(sizeof(node_t));if (newnode==NULL) {return -1;}newnode->data=data;newnode->next=p->next;p->next=newnode;;++table->cout;table->header=dummy.next;return 0;
}int insertChainListpos(ChainList_t *table, int pos, Element_t data) {node_t dummy;dummy.next=table->header;//判断边界条件if (data<0||pos>table->cout) {return -1;}//寻找到插入位置node_t *p=&dummy;int node=-1;while (node<pos-1) {p=p->next;node++;}//插入node_t *newnode=(node_t*)malloc(sizeof(node_t));newnode->data=data;newnode->next=p->next;p->next=newnode;++table->cout;table->header=dummy.next;return 0;}
int deleteChainListHeader(ChainList_t *table, Element_t data) {node_t dummy;dummy.next=table->header;node_t* p=&dummy;while (p->next&&p->next->data!=data) {p=p->next;}//判断是否为空if (p->next==NULL) {printf("No find");return-1;}//删除node_t*node=p->next;p->next=node->next;free(node);table->cout--;table->header=dummy.next;return 0;}void destoryChainList(ChainList_t *table) {node_t dummy;dummy.next=table->header;node_t* p=&dummy;node_t*node;while (p->next) {node=p->next;p->next=node->next;free(node);table->cout--;}printf("link list number %d",table->cout);}void showChainList(const ChainList_t *table) {node_t* p=table->header;printf("link list number %d\n:",table->cout);while (p) {printf(" %d ",p->data);p=p->next;}printf("\n");}

其实说实话跟之前的带链表的头指针一样,只不过需要先申请一个节点对节点做包装,而后维护一下表头指针的数据罢了。

我们再来做一下测试

int text2() {ChainList_t stu1;initChainList(&stu1);for (int i=0;i<10;i++) {insertChainListHeader(&stu1,i+100);}insertChainListpos(&stu1,3,300);showChainList(&stu1);destoryChainList(&stu1);}int main() {text2();return 0;
}

这就是对带头指针的链表的实现

但是如果我们再想提出一个需求,就是链表的最后的一个元素要指向第一个元素应该咋整。

这就是引出我们下一个主题

单项循环链表

单项循环链表

在开始我们的学习之前,我们可以先思考一个对代码

node_t*p=&header;
while(p->next)
{p=p->next;
}
……node_t*p=&header;
while(p)
{p=p->next
}

你思考就会发现,这两段代码是站在不同的角度进行遍历的,一个是对自己前一个元素进行看的,一个是对自己本身进行看的。

前者更适合插入和删除,而后者更适合遍历。

但是,一到循环链表这两段代码就不管用了,为啥?因为首尾连接了呗。

很自然的,也有两种循环链表的表示方法,一种是带头指针的,一种是不带头指针的

带头指针的

在这里插入图片描述

不带头指针的

在这里插入图片描述

很正常的我们先实现带头指针的

单项循环链表代码

结构定义以及函数接口


#ifndef LINKLOOP_H
#define LINKLOOP_H
typedef int Element;
//节点
typedef struct _loop_node {Element val;struct _loop_node *next;}loopnode;
//头节点
typedef struct {loopnode head;loopnode *tail;int num;
}LinkLoopList;//结构操作
//初始化
void initLinkLoopList(LinkLoopList *link_loop);
//插入(头插,尾插)
int insertLinkLoop(LinkLoopList *link_loop, Element val);
int inserLinkLoopRear(LinkLoopList *link_loop, Element val);
//遍历显示
void showLinkLoop(const LinkLoopList *link_loop);
//删除
int deleteLinkLoop(LinkLoopList *link_loop, Element val);
//释放空间不释放表头
void destoryLinkLoopRear(LinkLoopList *link_loop);#endif //LINKLOOP_H

结构操作的实现


#include "linkLoop.h"
#include <stdio.h>
#include <stdlib.h>void initLinkLoopList(LinkLoopList *link_loop) {//循环链表一开始就要指向自己link_loop->head.next=link_loop->tail=&link_loop->head;link_loop->num=0;}int insertLinkLoop(LinkLoopList *link_loop, Element val) {//先定义一个新节点loopnode*node=malloc(sizeof(loopnode));if (node==NULL)return -1;node->val=val;node->next=link_loop->head.next;link_loop->head.next=node;//判断是否维护尾指针if (link_loop->tail==&link_loop->head) {link_loop->tail=node;}link_loop->num++;return 0;}int inserLinkLoopRear(LinkLoopList *link_loop, Element val) {loopnode*node=malloc(sizeof(loopnode));if (node==NULL)return -1;node->val=val;node->next=link_loop->tail->next;link_loop->tail->next=node;link_loop->tail=node;link_loop->num++;return 0;}void showLinkLoop(const LinkLoopList *link_loop) {loopnode*node=link_loop->head.next;while (node!=&link_loop->head) {printf("%d->", node->val);node=node->next;}printf("\n");}int deleteLinkLoop(LinkLoopList *link_loop, Element val) {loopnode*node=&link_loop->head;while (node->next!=&link_loop->head&&node->next->val!=val) {node=node->next;}if (node->next->val==val) {loopnode *tmp=node->next;node->next=tmp->next;free(tmp);link_loop->num--;}else {printf("not found%d\n",val);}return 0;}void destoryLinkLoopRear(LinkLoopList *link_loop) {loopnode*node=link_loop->head.next;while (node!=&link_loop->head) {loopnode *tmp=node;node=node->next;free(tmp);link_loop->num--;}printf("Table %d element\n",link_loop->num);
}

测试函数


#include "linkLoop.h"void text1() {LinkLoopList table;initLinkLoopList(&table);for (int i = 0; i < 10; i++) {inserLinkLoopRear(&table, i+100);}deleteLinkLoop(&table,101);showLinkLoop(&table);destoryLinkLoopRear(&table);}int main() {text1();return 0;
}

结构如下

在这里插入图片描述

好了这篇博客到这里就结束了,喜欢的点点赞(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

八股文——JAVA基础:解释下什么是面向对象?面向对象和面向过程的区别

面向对象和面向过程是编程的不同思想&#xff1a; 面向过程如c语言的编程形式&#xff0c;在编程时定义的是一个方法&#xff0c;然后后续执行只需要关注这个方法的作用&#xff0c;而不会将方法进行抽象&#xff0c;也就是只关注程序执行的过程细节。 面向对象如java&#x…

SuperMap iServer 关闭数据目录(datacatalog)、地图打印(webprinting)等服务

背景 漏洞扫描发现有部分低危 web 漏洞&#xff0c;项目又暂未使用数据目录服务&#xff0c;所以最简单的方案是直接关闭服务。 查阅文档发现处理自动化服务可以修改webapps\iserver\WEB-INF\iserver-geoprocessing.xml 的 enable 属性为 false 关闭&#xff0c;机器学习服务…

PyTorch 张量(Tensors)全面指南:从基础到实战

文章目录 什么是张量&#xff1f;张量初始化方法1. 直接从数据创建2. 从 NumPy 数组转换3. 基于现有张量创建4. 使用随机值或常量 张量属性张量操作设备转移索引和切片连接张量算术运算单元素张量转换 原地操作&#xff08;In-place Operations&#xff09;PyTorch 与 NumPy 互…

Maven是什么?

Maven是一个流行的Java项目管理和构建工具&#xff0c;主要用于自动化项目构建、依赖管理和项目文档生成等工作。以下是对它的简单介绍&#xff1a; 核心功能 依赖管理&#xff1a;自动管理项目所需的第三方库&#xff08;如JAR包&#xff09;&#xff0c;通过在配置文件中声…

etcd教程-快速入门使用(截图实操)集群搭建 + 原理解释

大家好&#xff0c;我是此林。 etcd 是一个高可用的键值对存储系统&#xff0c;常用于分布式系统中保存配置、服务发现和协调信息。它是 CNCF 旗下的项目之一&#xff0c;也是 Kubernetes 的核心组件之一&#xff0c;用来存储集群状态。 可以说&#xff0c;云原生场景下经常使…

OpenSSL 混合加密

openssl 中文网&#xff1a; https://www.openssl.net.cn/ 目录 对称加密特点常见算法案例&#xff08;使用 AES&#xff09; 非对称加密特点常见算法案例&#xff08;使用 RSA&#xff09; 混合加密场景加密&#xff08;使用 AES&#xff09;解密 总结 对称加密 特点 加密和解…

AI驱动的DevOps运维与云服务部署自动化

引言 当前&#xff0c;云计算和DevOps实践让开发者能够管理成百上千台服务器和容器&#xff0c;但随之而来的运维复杂度也急剧提升。运维工程师经常需要部署多环境应用、维护大规模云主机、排查集群故障等任务。这些任务不仅涉及繁琐的脚本编写和命令行操作&#xff0c;还需要对…

Spring Boot动态数据源切换:优雅实现多数据源管理

在复杂的企业应用中&#xff0c;多数据源管理是常见需求。本文将介绍如何基于Spring Boot实现优雅的动态数据源切换方案&#xff0c;通过自定义注解和AOP实现透明化切换。 核心设计思路 通过三层结构实现数据源动态路由&#xff1a; 1. 注解层&#xff1a;声明式标记数据源 2…

如何挑选一款1588PTP时钟同步服务器​

在当今数字化程度极高的时代&#xff0c;高精度时间同步对于众多关键领域的高效、稳定运行起着决定性作用。PTP&#xff08;精确时间协议&#xff09;时钟作为实现高精度时间同步的核心设备&#xff0c;其性能优劣直接关乎系统整体表现。挑选一款合适的 ptp网络同步时钟&#x…

Harmony状态管理 @Local和@Param

深入理解ArkUI中的Param与Local装饰器 引言 在ArkUI的状态管理系统中&#xff0c;Param和Local是两个核心装饰器&#xff0c;它们分别用于处理组件间的数据传递和组件内部状态管理。本文将详细介绍这两个装饰器的使用场景、特性差异以及最佳实践。 Param装饰器&#xff1a;组…

物联网摄像头模块的应用场景

一、智慧城市治理 ‌智能交通优化‌ ‌动态信号控制‌&#xff1a;杭州部署20万物联网摄像头&#xff0c;实时分析车流密度并联动1200个红绿灯&#xff0c;早高峰通行效率提升40%。 ‌违规行为识别‌&#xff1a;搭载GB/T28181协议的摄像头AI抓拍交通违章&#xff0c;车牌识…

k8s Ingress、Service配置各样例大全

目录 壹、k8s Ingress 样例大全&#x1f527; 一、基础路由与 TLS 终止&#x1f504; 二、高级路由控制1. **URL 重写**&#xff08;适用后端服务路径与入口路径不一致&#xff09;2. **多路径路由到不同服务** &#x1f6a6; 三、流量治理策略1. **金丝雀发布&#xff08;灰度…

领域驱动设计(DDD)【10】之DDD战术模式:工厂模式与表意接口模式

文章目录 引言&#xff1a;DDD战术模式的重要性一、DDD中的工厂模式1.1 工厂模式的核心概念1.2 工厂模式的三种实现方式1.2.1 简单工厂方法1.2.2 工厂类1.2.3 抽象工厂模式 1.3 工厂模式的适用场景1.4 实际案例&#xff1a;电商订单系统 二、表意接口模式2.1 表意接口2.2 表意接…

鸿蒙ArkTS---登录逻辑,数据持久化,ArkUI,网络请求等基础内容记录

该内容是在【博学谷】学习过程中的代码记录&#xff0c;如有任何问题请与作者联系。 也欢迎同在学习鸿蒙开发的小伙伴的留言&#xff0c;一同学习&#xff0c;一同进步。 功能实现&#xff08;只记录代码&#xff0c;没有相关配置&#xff0c;跑不起来&#xff09;&#xff…

没有公网ip可以实现跨网p2p互通吗?内网让公网直连访问常用工具

没有公网IP的情况下仍然可以实现P2P通信&#xff0c;但需要借助NAT穿透技术或类似nat123同端口映射等第三方工具实现内网穿透‌。‌‌‌‌ 一、什么是P2P通信&#xff1f; P2P网络&#xff08;Peer-to-Peer Network&#xff09;是一种去中心化的网络架构&#xff0c;其中每个…

云服务器安装宝塔面板(BT Panel)

安装宝塔面板&#xff08;BT Panel&#xff09;是很多服务器管理员常用的操作&#xff0c;尤其适合用于管理网站、数据库、FTP等。以下是基于 Linux 系统&#xff08;推荐 CentOS 或 Ubuntu&#xff09;的宝塔面板安装步骤。 安装前准备 云服务器一台 可以订购服务器 海外云主…

mongoose解析http字段值

最近在使用mongoose开发嵌入式web后端时&#xff0c;会遇到要解析js前端发送过来的http消息&#xff0c;比如传递用户名&#xff0c;密码过来&#xff0c;后端要解析出来并判断是否登录成功。 前端http有两种组装字段的方式。 第一种是 $.ajax({url: /upgradePackage,method: P…

高德地图地址解析获取经纬度失败原因JSAPI

高德地图地址解析获取经纬度失败原因JSAPI 地图加载的时候老是报异常码&#xff0c;地图是可以加载出来的&#xff0c;但是在地图上的操作老是有异常码&#xff0c;找了好久不知道什么问题&#xff0c;异常码会报两种&#xff0c;一种是说什么key的问题&#xff0c;但是我当时…

极速JavaScript:全面性能优化实战指南

在现代Web开发中&#xff0c;JavaScript性能直接影响用户体验。一个优化良好的应用能带来更流畅的交互、更快的加载速度和更低的资源消耗。本文将深入探讨实用的JavaScript性能优化技术&#xff0c;帮助您打造高性能Web应用。 一、性能瓶颈分析与诊断工具 性能问题的常见来源&…

【开源模型】高考数学139分!小米MiMo开源模型:7B参数突出重围

小米 MiMo&#xff1a;7 B 参数撬动推理巅峰&#xff0c;开源模型的技术突围 70 亿参数超越 320 亿对手&#xff0c;高考数学 139 分的背后是训练策略的全面革新。 2025 年 4 月 30 日&#xff0c;小米开源的首个推理大模型 Xiaomi MiMo-7 B 横空出世&#xff0c;以​​仅 7 B …