循环链表 -- c语言实现
#pragma once
// 带头+双向+循环链表增删查改实现
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>typedef int LTDataType;typedef struct ListNode
{LTDataType data;struct ListNode* next;struct ListNode* prev;
}ListNode;//双链表申请一个新节点
ListNode* BuyListNode(LTDataType x);// 创建返回链表的头结点
ListNode* ListCreate();// 双向链表打印
void ListPrint(ListNode* pHead);// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);// 双向链表尾删
void ListPopBack(ListNode* pHead);// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);// 双向链表头删
void ListPopFront(ListNode* pHead);// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);// 双向链表销毁
void ListDestory(ListNode* pHead);//双链表申请一个新节点
ListNode* BuyListNode(LTDataType x)
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));if (node == NULL){perror("BuyListNode::malloc");return NULL;}node->data = x;node->next = NULL;node->prev = NULL;return node;
}// 创建返回链表的头结点
ListNode* ListCreate()
{ListNode* head = BuyListNode(-1);head->next = head;head->prev = head;
}// 双向链表打印
void ListPrint(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->next;printf("head<=>");while (cur != pHead){printf("%d<=>", cur->data);cur = cur->next;}printf("\n");return;
}// 双向链表尾插
// void ListPushBack(ListNode* pHead, LTDataType x)
// {
// assert(pHead);
// ListNode* newnode = BuyListNode(x);
// ListNode* tail = pHead->prev;
// tail->next = newnode;
// newnode->prev = tail;
// newnode->next = pHead;
// pHead->prev = newnode;
// return;
// }
// 下面的写法更容易理解
void ListPushBack(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode = BuyListNode(x);newnode->next = pHead;newnode->prev = pHead->prev;pHead->prev->next = newnode;pHead->prev = newnode;return;}// 双向链表尾删
void ListPopBack(ListNode* pHead)
{assert(pHead);ListNode* tail = pHead->prev;if (tail == pHead)return;tail->prev->next = pHead;pHead->prev = tail->prev;free(tail);tail = NULL;return;
}// 双向链表头插
// void ListPushFront(ListNode* pHead, LTDataType x)
// {
// assert(pHead);
// ListNode* newnode = BuyListNode(x);
// newnode->next = pHead->next;
// pHead->next->prev = newnode;
// pHead->next = newnode;
// newnode->prev = pHead;
// return;
// }void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* newnode = BuyListNode(x);newnode->next = pHead->next;newnode->prev = pHead;pHead->next->prev = newnode;pHead->next = newnode;return;
}// 双向链表头删
void ListPopFront(ListNode* pHead)
{assert(pHead);ListNode* tmp = pHead->next;if (tmp == pHead)return;tmp->next->prev = pHead;pHead->next = tmp->next;free(tmp);tmp = NULL;
}// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* cur = pHead;while (cur->data != x){cur = cur->next;if (cur == pHead)return NULL;}return cur;
}// 双向链表在pos的前面进行插入
// void ListInsert(ListNode* pos, LTDataType x)
// {
// assert(pos);
// ListNode* newnode = BuyListNode(x);
// pos->prev->next = newnode;
// newnode->prev = pos->prev;
// pos->prev = newnode;
// newnode->next = pos;
// return;
// }
// 下面这个更好理解,且避免了pos为头节点时若头节点的 next 为 NULL 时的错误
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = BuyListNode(x);newnode->next = pos;newnode->prev = pos->prev;pos->prev->next = newnode;pos->prev = newnode;return;
}// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{assert(pos);pos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;return;
}// 双向链表销毁
void ListDestory(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->next;while (cur != pHead){ListNode* tmp = cur->next;free(cur);cur = tmp;}free(pHead);return;
}int main() {// 创建一个双向循环链表,头节点headListNode* myList = ListCreate();// 尾插元素ListPushBack(myList, 1);ListPushBack(myList, 2);ListPushBack(myList, 3);// 头插元素ListPushFront(myList, 0);// 打印链表ListPrint(myList); // 应该输出:head<=>0<=>1<=>2<=>3<=>// 查找元素ListNode* foundNode = ListFind(myList, 2);if (foundNode != NULL) {printf("找到的节点的data: %d\n", foundNode->data); // 应该输出:Found node with value: 2} else {printf("没有找到\n");}// 在指定位置前插入元素ListInsert(foundNode, 10);// 打印链表ListPrint(myList); // 应该输出:head<=>0<=>1<=>10<=>2<=>3<=>// 删除指定位置的元素ListErase(foundNode);// 打印链表ListPrint(myList); // 应该输出:head<=>0<=>1<=>10<=>3<=>// 头删元素ListPopFront(myList);// 尾删元素ListPopBack(myList);// 打印链表ListPrint(myList); // 应该输出:head<=>1<=>2<=>// 打印 head 的 prev 的 dataprintf("head->prev->data: %d", myList->prev->data);// 销毁链表ListDestory(myList);return 0;
}
运行结果:

相关文章:
循环链表 -- c语言实现
#pragma once // 带头双向循环链表增删查改实现 #include<stdlib.h> #include<stdio.h> #include<assert.h>typedef int LTDataType;typedef struct ListNode {LTDataType data;struct ListNode* next;struct ListNode* prev; }ListNode;//双链表申请一个新节…...
如何使git提交的时候忽略一些特殊文件?
认识.gitignore文件 在生成远程仓库的时候我们会看到这样一个选项: 这个.gitignore文件有啥用呢? .gotignore文件是Git版本控制系统中的一个特殊文件。用来指定哪些文件或者目录不被Git追踪或者提交到版本库中。也就意味着,如果我们有一些文…...
如何保证Redis双写一致性?
目录 数据不一致问题 数据库和缓存不一致解决方案 1. 先更新缓存,再更新数据 该方案数据不一致的原因 2. 先更新数据库,再更新缓存 3. 先删除缓存,再更新数据库 延时双删 4. 先更新数据库,再删除缓存 该方案数据不一致的…...
HarmonyOS实战开发-如何实现查询当前城市实时天气功能
先来看一下效果 本项目界面搭建基于ArkUI中TS扩展的声明式开发范式, 数据接口是和风(天气预报), 使用ArkUI自带的网络请求调用接口。 我想要实现的一个功能是,查询当前城市的实时天气, 目前已实现的功能…...
(三)JSP教程——JSP动作标签
JSP动作标签 用户可以使用JSP动作标签向当前输出流输出数据,进行页面定向,也可以通过动作标签使用、修改和创建对象。 <jsp:include>标签 <jsp:include>标签将同一个Web应用中静态或动态资源包含到当前页面中。资源可以是HTML、JSP页面和文…...
centos7安装真的Redmine-5.1.2+ruby-3.0.0
下载redmine-5.1.2.tar.gz,上传到/usr/local/目录下 cd /usr/local/ tar -zxf redmine-5.1.2.tar.gz cd redmine-5.1.2 cp config/database.yml.example config/database.yml 配置数据连接 #编辑配置文件 vi config/database.yml #修改后的内容如下 product…...
方法的重写
方法的重写 概念:子类继承父类之后,就拥有了符合权限的父类的属性和方法,但是当父类的方法不符合子类的要求的时候,子类也可以重新的书写自己想要的方法。所以,方法的重写,即子类继承父类的方法后…...
Terraform局部值
Terraform输入变量用于从外部传递值到Terraform模块内部进行使用,如果把Terraform代码看作是一个函数的话,Terraform输入变量就是函数的输入参数。 Terraform局部值则用于在Terraform模块内部定义反复使用的常量值或表达式,如果把Terraform代…...
vue+element-ui实现横向长箭头,横向线上下可自定义文字(使用after伪元素实现箭头)
项目场景: 需要实现一个长箭头,横向线上下可自定义文字 代码描述 <div><span class"data-model">{{ //上方文字}}</span><el-divider class"q"> </el-divider>//分隔线<span class"data-mod…...
性能监控之prometheus+grafana搭建
前言 Prometheus和Grafana是两个流行的开源工具,用于监控和可视化系统和应用程序的性能指标。它们通常一起使用,提供了强大的监控和数据可视化功能。 Prometheus Prometheus是一种开源的系统监控和警报工具包。它最初由SoundCloud开发,并于…...
25-ESP32-S3 内置的真随机数发生器(RNG)
ESP32-S3 内置的真随机数发生器(RNG)😎 引言 📚 在许多应用中,随机数发生器(RNG)是必不可少的。无论是在密码学🔒、游戏🎮、模拟🧪或其他领域,随…...
万兆以太网MAC设计(12)万兆UDP协议栈上板与主机网卡通信
文章目录 一、设置IP以及MAC二、上板效果2.1、板卡与主机数据回环测试2.2、板卡满带宽发送数据 一、设置IP以及MAC 顶层模块设置源MAC地址 module XC7Z100_Top#(parameter P_SRC_MAC 48h01_02_03_04_05_06,parameter P_DST_MAC 48hff_ff_ff_ff_ff_ff )(input …...
2024年4月17日华为春招实习试题【三题】-题目+题解+在线评测,2024.4.17,华为机试
2024年4月17日华为春招实习试题【三题】-题目题解在线评测 🔮题目一描述:扑克牌消消乐输入描述输出描述样例一样例二Limitation解题思路一:模拟,遇到连续3张相同牌号的卡牌,直接删除解题思路二:栈解题思路三…...
展开说说:Android线程池解析
何谓线程池?本人理解是存放和管理线程的一个容器。 线程池存在的意义是什么? 第一:前面博客提到过创建和销毁线程的操作本身是有性能开销的,如果把使用的线程对象存起来下次用的时候直接取出来用就省去了一次创建和销毁的成本&a…...
Selenium自动化测试面试题全家桶
🔥 交流讨论:欢迎加入我们一起学习! 🔥 资源分享:耗时200小时精选的「软件测试」资料包 🔥 教程推荐:火遍全网的《软件测试》教程 📢欢迎点赞 👍 收藏 ⭐留言 …...
Docker 容器日志占用空间过大解决办法
1、vi /etc/docker/daemon.json {"log-driver":"json-file","log-opts": {"max-size":"200m", "max-file":"1"} } 2、重新加载守护进程配置文件 systemctl daemon-reload 3、重启docker systemctl…...
update_min_vruntime()流程图
linux kernel scheduler cfs的update_min_vruntime() 看起来还挺绕的。含义其实也简单,总一句话,将 cfs_rq->min_vruntime 设置为: max( cfs_rq->vruntime, min(leftmost_se->vruntime, cfs_rq->curr->vruntime) )。 画个流…...
十进制转任意进制(以及任意进制来回转换<了解>)
十进制转任意进制: #include <iostream> #include <vector> #include <string> using namespace std; // 将十进制数转换为P进制形式的字符串 string toBase(int num, int base) {string result ""; // 初始化结果字符串为空wh…...
postcss-px-to-viewport 从入坑到放弃 (nuxt3搭建响应式官网解决方案 )
前沿 什么是 postcss-px-to-viewport 将px单位转换为视口单位的 (vw, vh, vmin, vmax) 的 PostCSS 插件。 为什么使用 postcss-px-to-viewport 在pc端盛行的时代 ,如果你不想去适配更多的pc端代码,可以采用它。 由于nuxt3本身已带postcss,所…...
C语言从入门到入门
一、引言 C语言是一种通用的、过程式的计算机编程语言,支持结构化编程、词汇变量作用域和递归等功能,其设计提供了低级别的存取权限,并且要求程序员管理所有的内存细节。C语言具有高效、灵活和可移植性等特点,因此被广泛应用于系统编程、嵌入式系统开发、游戏开发等领域。 …...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
