当前位置: 首页 > news >正文

【数据结构】双向链表(真正的零基础)

链表是一种物理存储单元上非连续非顺序的存储结构。数据元素的逻辑顺序是通过指针的链接来实现的!在上篇我们学习了单向链表,而单向链表虽然空间利用率高,插入和删除也只需改变指针就可以达到!但是我们在每次查找、删除、访问.....时,都受到单向链表的一个特性干扰:单向不可逆,只能以遍历的形式去使用,这样使得需要从头到尾找到特定的节点再进行操作!今天我们来学习另外一种链表结构:双向链表。既然是零基础,我会从小白的角度进行讲解!放心食用!

目录

                                                            一.链表的分类

                                                   二.带头双向循环链表

                                                                               二.(1)链表节点设置

                                                                      二.(2)链表新增节点

                                                             二.(3)链表初始化 

                                                    二.(4)链表扩容节点

                                                    二.(5)链表打印

                                                    二.(6)在目标节点前面插入

                                                    二.(7)在目标节点后面插入

                                                    二.(8)链表头删

                                                    二.(9)链表尾删

                                                    二.(10)链表头插

                                                    二.(11)链表尾插

                                                    二.(12)删除中间目标节点

                                                    二.(13)释放双链表

               三.总结

               四.代码合并


一.链表的分类

这里就不介绍哈是链表了,如果对此感到疑惑,可以看我上一篇投稿,咱直接进入分类!

链表主要可以分为以下3大类:

单向链表:

单向链表是链表中最简单的一种,它由一系列节点组成,每个节点包含两个部分:指针域与数据域。指针域用来指向下一个节点的地址,数据域用来存储数据。以下是抽象草图,方便巧记:

双向链表:

 双向链表与单向链表不同,它的每个节点有两个指针(nextprev)跟一个数据域,两个指针分别指向前一个节点和后一个节点。同样,如图巧记:

循环链表:
 循环链表是一种特殊的单链表,它的最后一个节点的指针指向头节点,形成一个“环”,如图:

在进行下面的学习之前,针对链表的各种分类,我们需要理清几个名词:

头指针:

头指针本质上是结构体类型的指针,指向一个节点,这个节点一般是 第一个节点 或者 头节点

头结点:

简单理解为在 第一个节点 前面还有一个节点,这个节点就称为头节点。头节点一般不存储数据

带头:

头指针指向头节点就称为带头节点,如图:

不带头: 

头指针指向第一个节点就称为不带头节点,如图:

针对这几种分类:带头或者不带头、单向或者双向、循环或者非循环 

我们总共可以组合八种链表,我们主要学习 无头双向非循环链表     带头双向循环链表    这两种!

八种链表结构中其中这两种最具代表性,另外六种便自然理解了!上一篇我们已经学完了第一种,今天我们来学习第二种!

注意:双链表一般需要改变四个指针的指向,因此建议大家画图理解!这样就可以轻松操作了!

二.带头双向循环链表

理解了上面几个名词后,我们就能轻易理解哈是带头双向循环链表了!

带头:无非就是第一个节点前还有一个头节点嘛!

双向:节点里面有两个指针,两个指针分别指向一前一后的节点

循环:尾节点的后指针指向头节点,头节点的前指针指向尾节点,达到封闭,形成循环!

二.(1)链表节点设置

不论怎样,我们都需要先创建一个结构体作为接下来各种功能的节点基础, 这个结构体里面有两个指针,一个数据域。这里为了缩短结构体类型,我们用 typedef 重定义以下,需要注意的是typedef重定义的这个结构体里面,结构体指针必须写完整,因为此处还属于声明阶段,后续才可以正常缩写。代码如下:

typedef struct Pointdef
{struct Pointdef* next;//指向下一个节点struct Pointdef* prev;//指向上一个节点int data;//数据域
}Pointdef;
二.(2)链表新增节点

因为我们不管初始化还是各种增添功能,都需要节点,所以我们先完成开辟节点的函数,让这个函数返回指向这个开辟好的节点指针,然后才可以进行初始化操作,代码如下:

//开辟新节点
Pointdef* Newspoint(int x)
{Pointdef* news = (Pointdef*)malloc(sizeof(Pointdef));if (news == NULL){perror("malloc");return NULL;}//初始化新节点news->next = NULL;news->prev = NULL;news->data = x;return news;
}

我们刚开辟好的节点还没有连接,只是一个结构体大小,因此它的prevnext指针都指向NULLx就是初始化节点的数据域,如下图理解:

二.(3)链表初始化 

上面我们先调用 新增节点 的函数,在外面开辟一个节点作为头节点(如果有问题,可以先看下面的合并代码!),接下来对这个头节点进行初始化,将它作为头节点,此时头节点的nextprev指针都应该指向自己(因为是循环链表,此时只有一个节点)。代码如下:

//链表初始化
void Initialize(Pointdef* head)
{head->next = head;head->prev = head;
}

二.(4)链表扩容节点

此时我们已经初始化了链表,下面我们给它新增几个节点,调用新增函数,改变节点指针指向,代码如下:

//链表扩容节点
Pointdef* tail = head;
for (int i = 2; i <= 5; i++)
{Pointdef* news = Newspoint(i);//连接tail->next = news;news->prev = tail;news->next = head;head->prev = news;tail = tail->next;
}

二.(5)链表打印

与单向链表访问区别不大,只需要注意是这里的链表是循环的,需要控制打印的条件不能是当前指针不能指向头节点,否则就是死循环了,代码如下: 

//链表打印
void Printf(Pointdef* head)
{Pointdef* tail = head->next;while (tail != head){printf("%d <= ", tail->data);tail = tail->next;}printf("NULL\n");
}

二.(6)在目标节点前面插入

我们已经写好了一条双链表,那么我们只需要找到目标节点然后连接四个指针就行了,代码如下:

这里我们假设目标节点是 tail ,新节点 news  新节点前面的节点是 prev

//在目标节点前面插入
void FrontInsertion(Pointdef* tail ,int x)
{assert(tail);Pointdef* prev = tail->prev;Pointdef* news = Newspoint(x);prev->next = news;news->prev = prev;news->next = tail;tail->prev = news;}

二.(7)在目标节点后面插入

将找到的目标节点作为参数,然后在函数中改变它的前后四个指针就行,如图:

//在目标节点后面插入
void OutInsertion(Pointdef* tail, int x)
{assert(tail);//目标节点的下一个节点不能是空,否则就是尾插了assert(tail->next);Pointdef* news = Newspoint(x);Pointdef* next = tail->next;tail->next = news;news->prev = tail;news->next = next;next->prev = news;
}


 二.(8)链表头删

头删咱直接将头节点作为参数就行了,再记录第二个节点方便释放,然后用四个指针连接头节点和第二个节点,最后记得释放第一个节点的空间,如图:

//链表头删
void Outomit(Pointdef* head,int x)
{assert(head);assert(head->next);Pointdef* tail = head->next->next;Pointdef* next = head->next;head->next = tail;tail->prev = head;free(next);next = NULL;
}

二.(9)链表尾删

这个咱就不多说了!就是先记录倒数第二个节点,将它作为尾节点就行了,同样记得释放空间,直接上代码:

//链表尾删
void Tailomit(Pointdef* head, int x)
{assert(head);Pointdef* prev = head->prev->prev;Pointdef* tail = head->prev;prev->next = head;head->prev = prev;free(tail);tail = NULL;
}

二.(10)链表头插

但凡插入节点的都具有很大的相似之处,无非就是将这个节点插入某处,通过记录附近节点的位置,再把新增节点的左右节点与它进行连接,看代码:

//链表头插
void Headinsert(Pointdef* head, int x)
{assert(head);Pointdef* tail = head->next;Pointdef* news = Newspoint(x);head->next = news;news->prev = head;news->next = tail;tail->prev = news;
}

二.(11)链表尾插
//链表尾插
void Tailinsert(Pointdef* head, int x)
{assert(head);Pointdef* tail = head->prev;Pointdef* news = Newspoint(x);tail->next = news;news->prev = tail;head->prev = news;news->next = head;
}

二.(12)删除中间目标节点

删除节点我们肯定要先找到目标节点,我们在链表中可以通过以下两种方式寻找:指针域  数据域,在上面的前后插入中,我们选择的指针查找方式,下面我们进行数据查找方式来删除:

//链表中间节点删除
void Omit(Pointdef* tail)
{Pointdef* pc = tail->prev;Pointdef* pt = tail->next;pc->next = pt;pt->prev = pc;free(tail);tail = NULL;
}

二.(13)释放双链表

咋从第一个节点释放到尾就OK了!注意:链表的空间不是连续的,因此需要一个个销毁!

//双链表销毁
void Undermine(Pointdef* head)
{assert(head);Pointdef* cur = head->next;Pointdef* tail = cur->next;while (cur != head){tail = cur->next;free(cur);cur = tail;}free(head);head=NULL;printf("释放完毕\n");
}
三.总结

针对以上实现的双链表,其实针对整个链表,都有以下特点:

1:首先我们要有设计好开辟节点的函数,然后知道如何连接这些节点

2:头插、尾插、中间插其实都有很大的共性,只是目标地点稍微差异,这点大家可以画图理解!

3:删除某个节点需要及时释放该空间,避免空间泄漏。需要注意是先连接,再释放

4:针对为何单向链表很多涉及二级指针,而双向链表大多是一级指针?

因为单向链表有很多地方需要改变头指针(单向链表将第一个节点作为头节点)的指向,而双向链表是固定了哨兵节点,因此其它操作其实是针对结构体里面的指针操作的。该知识的原理是:对形参的改变不影响实参,如需改变形参,需要使用二级指针

5:双向链表为哈要从第一个节点开始释放?

双向链表的头节点是链表结构的一部分,不是数据节点,应该先释放数据节点,再释放链表结构

四.代码合并

流程文件(可以使用打印来进行功能的测试!)

#define _CRT_SECURE_NO_WARNINGS 1
#include"text.h"int main()
{int x = 4;//链表新增节点Pointdef* head=Newspoint(1);//链表初始化void Initialize(head);//链表扩容节点Pointdef* tail = head;for (int i = 2; i <= 5; i++){Pointdef* news = Newspoint(i);//连接tail->next = news;news->prev = tail;news->next = head;head->prev = news;tail = tail->next;}//链表打印Printf_t(head);//在目标节点前面插入                      tail = (head->next)->next;x = 6;FrontInsertion(tail, x);//在目标节点后面插入                OutInsertion(tail, x);//                                  测试点Printf_t(head);//链表头删x = 7;Outomit(head,x);//链表尾删x = 8;Tailomit(head, x);//                                  测试点Printf_t(head);//链表头插x = 1;Headinsert(head, x);//链表尾插x = 9;Tailinsert(head, x);//                                  测试点Printf_t(head);//链表中间节点删除x = 4;tail = head -> next;while (tail->data != x){tail = tail->next;}Omit(tail);//                                   测试点Printf_t(head);//双链表销毁Undermine(head);return 0;
}

头文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
#include<stdio.h>
#include<assert.h>typedef struct Pointdef
{struct Pointdef* next;//指向下一个节点struct Pointdef* prev;//指向上一个节点int data;//数据域
}Pointdef;//链表新增节点
Pointdef* Newspoint(int x);
//链表初始化
void Initialize(Pointdef* head);
//链表打印
void Printf_t(Pointdef* head);
//在目标节点前面插入
void FrontInsertion(Pointdef* tail, int x);
//在目标节点后面插入
void OutInsertion(Pointdef* tail, int x);
//链表头删
void Outomit(Pointdef* head ,int x);
//链表尾删
void Tailomit(Pointdef* head, int x);
//链表头插
void Headinsert(Pointdef* head, int x);
//链表尾插
void Tailinsert(Pointdef* head, int x);
//链表中间节点删除
void Omit(Pointdef* tail);
//双链表销毁
void Undermine(Pointdef* head);

函数执行文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"text.h"//链表新增节点
Pointdef* Newspoint(int x)
{Pointdef* news = (Pointdef*)malloc(sizeof(Pointdef));if (news == NULL){perror("malloc");return NULL;}//初始化新节点news->next = NULL;news->prev = NULL;news->data = x;return news;
}//链表初始化
void Initialize(Pointdef* head)
{head->next = head;head->prev = head;
}//链表打印
void Printf_t(Pointdef* head)
{Pointdef* tail = head->next;while (tail != head){printf("%d <= ", tail->data);tail = tail->next;}printf("NULL\n");
}//在目标节点前面插入
void FrontInsertion(Pointdef* tail ,int x)
{assert(tail);Pointdef* prev = tail->prev;Pointdef* news = Newspoint(x);prev->next = news;news->prev = prev;news->next = tail;tail->prev = news;}//在目标节点后面插入
void OutInsertion(Pointdef* tail, int x)
{assert(tail);//目标节点的下一个节点不能是空,否则就是尾插了assert(tail->next);Pointdef* news = Newspoint(x);Pointdef* next = tail->next;tail->next = news;news->prev = tail;news->next = next;next->prev = news;
}//链表头删
void Outomit(Pointdef* head,int x)
{assert(head);assert(head->next);Pointdef* tail = head->next->next;Pointdef* next = head->next;head->next = tail;tail->prev = head;free(next);next = NULL;
}//链表尾删
void Tailomit(Pointdef* head, int x)
{assert(head);Pointdef* prev = head->prev->prev;Pointdef* tail = head->prev;prev->next = head;head->prev = prev;free(tail);tail = NULL;
}//链表头插
void Headinsert(Pointdef* head, int x)
{assert(head);Pointdef* tail = head->next;Pointdef* news = Newspoint(x);head->next = news;news->prev = head;news->next = tail;tail->prev = news;
}//链表尾插
void Tailinsert(Pointdef* head, int x)
{assert(head);Pointdef* tail = head->prev;Pointdef* news = Newspoint(x);tail->next = news;news->prev = tail;head->prev = news;news->next = head;
}//链表中间节点删除
void Omit(Pointdef* tail)
{Pointdef* pc = tail->prev;Pointdef* pt = tail->next;pc->next = pt;pt->prev = pc;free(tail);tail = NULL;
}//双链表销毁
void Undermine(Pointdef* head)
{assert(head);Pointdef* cur = head->next;Pointdef* tail = cur->next;while (cur != head){tail = cur->next;free(cur);cur = tail;}free(head);head = NULL;printf("释放完毕\n");
}

相关文章:

【数据结构】双向链表(真正的零基础)

链表是一种物理存储单元上非连续、非顺序的存储结构。数据元素的逻辑顺序是通过指针的链接来实现的&#xff01;在上篇我们学习了单向链表&#xff0c;而单向链表虽然空间利用率高&#xff0c;插入和删除也只需改变指针就可以达到&#xff01;但是我们在每次查找、删除、访问..…...

【生产变更】- Oracle RAC添加配置ipv6地址

【生产变更】- Oracle RAC添加配置ipv6地址 一、概述二、环境检查及备份2.1 检查并备份系统层面IP配置2.2 检查并备份监听配置2.3 检查并备份网卡配置2.4 检查并备份/etc/hosts三、集群层面配置3.1 检查集群配置3.2 停止集群组件3.3 Bond0网卡设置3.4 /etc/hosts文件配置3.5 重…...

Ai无限免费生成高质量ppt教程(deepseek+kimi)

第一步&#xff1a;打开deepseek官网&#xff08;DeepSeek) 1.如果deepseek官网网络繁忙&#xff0c;解决方案如下&#xff1a; (1)使用easychat官网&#xff08;EasyChat&#xff09;使用deepseek模型&#xff0c;如图所示&#xff1a; &#xff08;2&#xff09;本地部署&…...

python全栈-python基础

python基础 文章目录 python基础python入门基础概念序列列表元组 -- 不可变序列字典字典的本质集合 控制语句选择结构 - 条件判断结构循环结构zip()推导式 函数及原理参数LEGB规则 面向对象私有属性和私有方法面向对象的特征重写__str__()方法super获得父类的定义特殊方法和运算…...

Python 鼠标轨迹 - 防止游戏检测

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…...

力扣 零钱兑换

完全背包&#xff0c;动态规划例题。 题目 这题跟完全背包跟完全平方数有点相似。在完全平方数中&#xff0c;用一个dp数组去取得目标金额的每一步的最优&#xff0c;当前状态可能来自上一个dp&#xff0c;也有可能比上一个dp更小&#xff0c;因此往回退一步加一做比较。在完全…...

C# OpenCV机器视觉:OSTU算法实现背景差分的自适应分割

在一个热闹的科技公司里&#xff0c;阿强是一个负责图像分析的员工。他的日常工作就是从各种复杂的图像中提取出有用的信息&#xff0c;可这可不是一件轻松的事情哦 最近&#xff0c;阿强接到了一个艰巨的任务&#xff1a;要从一堆嘈杂的监控图像中分离出运动的物体&#xff0c…...

快速搭建 Elasticsearch 8 集群:零基础实战与升级注意事项

引言 随着大数据技术的飞速发展,Elasticsearch 成为许多应用场景中不可或缺的技术,它以其高效的全文搜索引擎和分布式存储架构在企业和个人项目中占据了一席之地。无论是在日志分析、实时搜索还是数据可视化中,Elasticsearch 都发挥着重要的作用。 在这篇文章中,我们将为…...

基于扑克牌分发效果制作时的问题总结

其基本效果如图 1. 在overlay模式下直接使用position来移动 实现代码 public class Card : MonoBehaviour {public RectTransform target;public Button cardButton;private bool isPack false;public List<RectTransform> cards new List<RectTransform>(…...

老榕树的Java专题:Redis 从入门到实践

一、引言 在当今的软件开发领域&#xff0c;数据的高效存储和快速访问是至关重要的。Redis&#xff08;Remote Dictionary Server&#xff09;作为一个开源的、基于内存的数据结构存储系统&#xff0c;因其高性能、丰富的数据类型和广泛的应用场景&#xff0c;成为了众多开发者…...

【玩转 Postman 接口测试与开发2_019】第15章:利用 Postman 初探 API 性能测试(含实战截图)

《API Testing and Development with Postman》最新第二版封面 文章目录 第十五章 API 接口性能测试1 性能负载的类型2 Postman 负载配置3 Postman 性能测试实战3.1 Fixed 型负载下的性能测试3.2 基于数据驱动的 Postman 接口性能测试 4 性能测试的注意事项 写在前面 终于来到了…...

在 Qt 开发中,可以将 QML 封装成库

在 Qt 开发中&#xff0c;可以将 QML 封装成库&#xff0c;以便在多个项目中复用 QML 组件或模块。下面通过一个简单的例子说明如何将 QML 封装成库并在其他项目中使用。 1. 创建 QML 库项目 首先&#xff0c;我们创建一个新的 Qt 项目&#xff0c;专门用于封装 QML 组件。假…...

换电脑了如何快速导出vscode里的插件

当你换电脑了&#xff0c;之前vscode里的插件又不想全部手动重装&#xff0c;那么恭喜你&#xff0c;刷到了这篇文章。 1. 将 VSCode 添加到系统路径 macOS 打开 VSCode。按下 Command Shift P 打开命令面板。 3。 输入 Shell Command: Install ‘code’ command in PATH …...

点大商城V2-2.6.6源码全开源uniapp +搭建教程

一.介绍 点大商城V2独立开源版本&#xff0c;版本更新至2.6.6&#xff0c;系统支持多端&#xff0c;前端为UNiapp&#xff0c;多端编译。 二.搭建环境&#xff1a; 系统环境&#xff1a;CentOS、 运行环境&#xff1a;宝塔 Linux 网站环境&#xff1a;Nginx 1.21 MySQL 5.…...

9 Pydantic复杂数据结构的处理

在构建现代 Web 应用时&#xff0c;我们往往需要处理复杂的输入和输出数据结构。例如&#xff0c;响应数据可能包含嵌套字典、列表、元组&#xff0c;甚至是多个嵌套对象。Pydantic 是一个强大的数据验证和序列化库&#xff0c;可以帮助我们轻松地处理这些复杂的数据结构&#…...

springboot+redis实现将树形结构存储到redis

1.pom配置redis <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>2.yml文件配置&#xff1a; spring:redis:database: 0host: 1.1.1.1port: 6379timeout:…...

6、使用one-api管理统一管理大模型,并开始使用本地大模型

文章目录 本节内容介绍集中接入&#xff1a;将大模型统一管理起来当使用了大模型代理大模型代理示例 开源模型&#xff1a;如何使用Hugging Face上的模型modelscope使用 pipeline 调用模型用底层实现调用模型流式输出 如何在项目中使用开源模型使用 LangChain使用集中接入开始使…...

Windows安装Lyx

Lyx介绍 LyX 是一个基于 LaTeX 的可视化编辑器&#xff0c;可以让在不编写 LaTeX 代码的情况下使用 LaTeX 的排版功能。 需要依赖Latex环境&#xff0c;如Tex live 或者 MiKTeX Lyx 官网 Lyx官网 安装包下载 点击download默认进入最新版本下载界面 在Recent News/ News里可选…...

一文讲透大模型部署工具ollama--结合本地化部署deepseek实战

Ollama 是一个开源的人工智能平台&#xff0c;专注于在本地高效运行大型语言模型&#xff08;LLMs&#xff09;。通过 Ollama&#xff0c;开发者可以在自己的机器上运行多种大规模语言模型&#xff0c;而不必依赖于云端服务。它支持对大模型的管理和本地化部署&#xff0c;并且…...

网络防御高级

接口配置&#xff1a; SW2: [sw2]vlan 10 [sw2]vlan 20 [sw2]interface GigabitEthernet 0/0/1 [sw2-GigabitEthernet0/0/1]port link-type trunk [SW2-GigabitEthernet0/0/1]port trunk allow-pass vlan 10 20 [sw2]interface GigabitEthernet 0/0/2 [sw2-GigabitEthernet0/0/…...

使用PyCharm进行Django项目开发环境搭建

如果在PyCharm中创建Django项目 1. 打开PyCharm&#xff0c;选择新建项目 2.左侧选择Django&#xff0c;并设置项目名称 3.查看项目解释器初始配置 4.新建应用程序 执行以下操作之一&#xff1a; 转到工具| 运行manage.py任务或按CtrlAltR 在打开的manage.pystartapp控制台…...

如何定义“破坏环境”

当我们谈论破坏环境时&#xff0c;通常会从人类活动对自然生态造成负面影响的角度来定义。例如&#xff0c;大规模的森林砍伐、工业污染排放、温室气体增加等&#xff0c;都是典型的破坏环境的行为。我们常常看到这些行为导致了生态系统的破坏、物种灭绝、气候变化等问题&#…...

现代前端开发的演进与未来趋势:从工具革新到技术突破

在过去的十年中&#xff0c;前端开发经历了翻天覆地的变化。从最初的静态页面到如今复杂的单页应用&#xff08;SPA&#xff09;&#xff0c;从手动操作 DOM 到基于虚拟 DOM 的高效渲染&#xff0c;从前端“三剑客”&#xff08;HTML/CSS/JS&#xff09;到全栈框架的兴起&#…...

活动预告 |【Part1】Microsoft 安全在线技术公开课:安全性、合规性和身份基础知识

课程介绍 通过参加“Microsoft 安全在线技术公开课&#xff1a;安全性、合规性和身份基础知识”活动提升你的技能。在本次免费的介绍性活动中&#xff0c;你将获得所需的安全技能和培训&#xff0c;以创造影响力并利用机会推动职业发展。你将了解安全性、合规性和身份的基础知识…...

idea Ai工具通义灵码,Copilot我的使用方法以及比较

我用过多个idea Ai 编程工具&#xff0c;大约用了1年时间&#xff0c;来体会他们那个好用&#xff0c;以下只是针对我个人的一点分享&#xff0c;不一定对你适用 仅作参考。 介于篇幅原因我觉得能说上好用的 目前只有两个 一个是阿里的通义灵码和Copilot&#xff0c;我用它来干…...

【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter8-对象、类与面向对象编程

八、对象、类与面向对象编程 ECMA-262 将对象定义为一组属性的无序集合。严格来说&#xff0c;这意味着对象就是一组没有特定顺序的 值。对象的每个属性或方法都由一个名称来标识&#xff0c;这个名称映射到一个值。正因为如此&#xff08;以及其他还未讨论的原因&#xff09;&…...

介绍下SpringBoot常用的依赖项

Spring Boot 是一个用于快速开发 Spring 应用程序的框架&#xff0c;它通过自动配置和依赖管理简化了开发过程。以下是一些 Spring Boot 项目中常用的依赖项&#xff1a; 1. Spring Boot Starter Web 作用: 用于构建 Web 应用程序&#xff0c;包括 RESTful 服务。依赖项: spr…...

深度解析策略模式:从理论到企业级实战应用

一、策略模式的本质&#xff1a;面向接口的算法工厂 策略模式&#xff08;Strategy Pattern&#xff09;是行为型设计模式的典型代表&#xff0c;其核心思想是将算法族抽象为独立对象&#xff0c;使其能够相互替换。这种模式完美体现了以下面向对象设计原则&#xff1a; 开闭原…...

【Linux】深入理解linux权限

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;Linux 目录 前言 一、权限是什么 二、用户和身份角色 三、文件属性 1. 文件属性表示 2. 文件类型 3. 文件的权限属性 四、修改文件的权限属性和角色 1. …...

C++STL(六)——list模拟

目录 本次所需实现的三个类一、结点类的模拟实现构造函数 二、迭代器类的模拟实现为什么有迭代器类迭代器类的模板参数说明构造函数运算符的重载- -运算符的重载和!运算符的重载*运算符的重载->运算符的重载引入模板第二个和第三个参数 三、list的模拟实现3.1 默认成员函数构…...