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

【数据结构】线性表之链表

目录

  • 前言
  • 一、链表的定义
  • 二、链表的分类
    • 1. 单向和双向
    • 2. 带头和不带头
    • 3. 循环和不循环
    • 4. 常用(无头单向非循环链表和带头双向循环链表)
  • 三、无头单向非循环链表的接口及实现
    • 1. 单链表的接口
    • 2. 接口的实现
  • 四、带头双向循环链表接口的及实现
    • 1. 双向链表的接口
  • 2. 接口的实现
  • 五、带头双向循环链表VS无头单向非循环链表
    • 1. 带头双向循环链表
      • 1.1 带头双向循环链表的优点:
      • 1.2 带头双向循环链表的缺点:
    • 2. 无头单向非循环链表
      • 2.1 无头单向非循环链表的优点:
      • 2.2 无头单向非循环链表的缺点:
    • 3. 小结
  • 六、链表VS顺序表
    • 1. 带头双向循环链表
      • 1.1 链表的优点:
      • 2. 链表的缺点:
    • 2. 顺序表
      • 2.1顺序表的优点
      • 2.2顺序表的缺点
  • 结尾

前言

上一篇文章讲述了线性表中的顺序表,这篇文章讲述关于链表的定义、类别、实现、多种不同链表的优缺点和链表与顺序表的优缺点。
关于上一篇文章的链接:线性表之顺序表

一、链表的定义

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

在这里插入图片描述

二、链表的分类

1. 单向和双向

在这里插入图片描述

在这里插入图片描述

2. 带头和不带头

在这里插入图片描述
在这里插入图片描述

3. 循环和不循环

在这里插入图片描述
在这里插入图片描述

4. 常用(无头单向非循环链表和带头双向循环链表)

在这里插入图片描述

在这里插入图片描述

三、无头单向非循环链表的接口及实现

1. 单链表的接口

#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>// slist.h
typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode* plist);

2. 接口的实现

#include "slist.h"SListNode* BuySListNode(SLTDateType x)
{//创造新节点SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc");return NULL;}//新节点初始化newnode->data = x;newnode->next = NULL;return newnode;
}void SListPushBack(SListNode** pplist, SLTDateType x)
{//这里使用二级指针的原因是://若链表为空,需要改变的就是结构体指针,需要结构体指针的地址//若传入的是一级指针,这里传入的只是临时拷贝,无法改变函数外的变量if (*pplist == NULL){*pplist = BuySListNode(x);}//若不为空,需要改变的是结构体,只需要结构体的指针else{SListNode* tail = *pplist;SListNode* newnode = BuySListNode(x);while (tail->next){tail = tail->next;}tail->next = newnode;}}void SListPrint(SListNode* plist)
{while (plist){printf("%d->", plist->data);plist = plist->next;}printf("NULL");
}void SListPushFront(SListNode** pplist, SLTDateType x)
{//这里使用二级指针的原因是:每次头删都需要改变头节点SListNode* newnode = BuySListNode(x);newnode->next = *pplist;*pplist = newnode;
}//无头单链表尾删删除节点的时候有三种情况
void SListPopBack(SListNode** pplist)
{//没有节点assert(*pplist);//一个节点if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}//多个节点else{//尾删既可以找尾找尾的前一个节点,也可以创造一个变量记录尾节点的前一个节点//创造一个变量记录尾节点的前一个节点//	SListNode* tail = *pplist;//	SListNode* prev = NULL;//	while (tail->next)//	{//		prev = tail;//		tail = tail->next;//	}//	free(prev->next);//	prev->next = NULL;//找尾找尾的前一个节点SListNode* tail = *pplist;while (tail->next->next){tail = tail->next;}free(tail->next);tail->next = NULL;}
}//无头单链表头删删除节点的时候有三种情况,但只有一个节点和多个节点的情况可以合并
void SListPopFront(SListNode** pplist)
{//无节点时assert(*pplist);//有节点时SListNode* del = *pplist;*pplist = del->next;free(del);
}SListNode* SListFind(SListNode* plist, SLTDateType x)
{SListNode* cur = plist;while (cur){if (cur->data == x)return cur;cur = cur->next;}return NULL;
}void SListInsertAfter(SListNode* pos, SLTDateType x)
{//断言:在pos后面插入一个节点,最差的情况是pos为尾节点,但不能为NULLassert(pos);SListNode* cur = (SListNode*)malloc(sizeof(SListNode));if (cur == NULL){perror("malloc");return;}cur->data = x;cur->next = pos->next;pos->next = cur;
}void SListEraseAfter(SListNode* pos)
{//需要删除节点的前一个节点不能为NULL,删除节点也不能为NULLassert(pos);assert(pos->next);SListNode* next = pos->next;pos->next = next->next;free(next);
}void SListDestroy(SListNode* plist)
{SListNode* del = NULL;while (plist){del = plist;plist = plist->next;free(del);}
}

四、带头双向循环链表接口的及实现

1. 双向链表的接口

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType data;struct ListNode* next;struct ListNode* prev;
}ListNode;// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
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);

2. 接口的实现

#include "list.h"// 创建返回链表的头结点.
ListNode* ListCreate()
{ListNode* phead = (ListNode*)malloc(sizeof(ListNode));if (phead == NULL){perror("malloc");return NULL;}//让头的 next 和 prev 都指向自己//则双向链表为空phead->next = phead;phead->prev = phead;return phead;
}// 创造新节点
ListNode* BuyTLNode(LTDataType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc");return NULL;}newnode->data = x;return newnode;
}// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{/*ListNode* tail = pHead->prev;ListNode* newnode = BuyTLNode(x);newnode->next = pHead;newnode->prev = tail;tail->next = newnode;pHead->prev = newnode;*/ListInsert(pHead, x);   //复用
}// 双向链表打印
void ListPrint(ListNode* pHead)
{printf("header <--> ");//打印的时候,由于头内的 data 的值没用//则从头的的下一个节点开始打印//并且在循环到头的时候打印结束ListNode* cur = pHead ->next;while (cur != pHead){printf("%d <--> ", cur->data);cur = cur->next;}printf("\n");
}// 判断双向链表是否为空
bool LTEmpty(ListNode* pHead)
{assert(pHead);return pHead->next == pHead;
}// 双向链表头删
void ListPopFront(ListNode* pHead)
{assert(pHead);assert(!(LTEmpty(pHead)));/*ListNode* first = pHead->next;ListNode* second = first->next;pHead->next = second;second->prev = pHead;free(first);*/ListErase(pHead->next);   //复用
}// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{/*ListNode* next = pHead->next;ListNode* newnode = BuyTLNode(x);newnode->next = next;newnode->prev = pHead;next->prev = newnode;pHead->next = newnode;*/ListInsert(pHead->next, x);//复用
}// 双向链表尾删
void ListPopBack(ListNode* pHead)
{assert(pHead);               assert(!(LTEmpty(pHead)));/*ListNode* tail = pHead->prev;ListNode* tailPrev = tail->prev;tailPrev->next = pHead;pHead->prev = tailPrev;free(tail);*/ListErase(pHead->prev); //复用
}// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{ListNode* cur = pHead ->next;while (cur){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = BuyTLNode(x);ListNode* posPrev = pos->prev;newnode->next = pos;newnode->prev = posPrev;posPrev->next = newnode;pos->prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{assert(pos);ListNode* posPrev = pos->prev;ListNode* posNext = pos->next;posPrev->next = posNext;posNext->prev = posPrev;free(pos);
}

五、带头双向循环链表VS无头单向非循环链表

1. 带头双向循环链表

1.1 带头双向循环链表的优点:

  1. 可以自由地在链表中任意位置插入和删除节点,这是因为双向链表可以方便地找到前驱后继节点
  2. 可以支持双向遍历,即可以从前往后或从后往前遍历链表。
  3. 可以更加高效地实现某些特殊的操作,比如在链表中删除指定节点,需要同时修改其前驱和后继节点的指针,双向链表则可以直接完成这个操作,而单向链表则需要遍历到前驱节点才能完成。

1.2 带头双向循环链表的缺点:

  1. 因为每个节点都要额外存储一个前驱节点的指针,所以需要更多的内存空间。
  2. 因为需要维护前驱和后继节点的指针,所以在插入和删除节点时需要更多的操作,导致时间复杂度较高。

2. 无头单向非循环链表

2.1 无头单向非循环链表的优点:

  1. 因为每个节点只需存储一个后继节点的指针,所以需要较少的内存空间。
  2. 在插入和删除节点时,只需要修改前一个节点的指针,不需要修改后一个节点的指针,操作相对简单,导致时间复杂度较低。

2.2 无头单向非循环链表的缺点:

  1. 无法实现双向遍历,即无法从后往前遍历链表。
  2. 在删除指定节点时,需要先遍历到其前驱节点,才能完成删除操作,导致删除效率较低。

3. 小结

总之,选择哪种链表数据结构应该根据具体的应用场景和需要做的操作来决定。如果需要频繁地插入和删除节点,且需要支持双向遍历,可以选择带头双向循环链表;如果需要占用较少的内存空间,且不需要双向遍历,可以选择无头单向非循环链表。


六、链表VS顺序表

1. 带头双向循环链表

1.1 链表的优点:

  1. 动态内存分配:链表可以在运行时动态地分配内存,因此可以根据实际需要灵活地增加或减少节点数。
  2. 插入和删除操作高效:由于链表中的元素不必在连续的内存空间中存储,所以插入和删除操作非常高效,只需要修改指针,而不需要移动所有后续元素。
  3. 大小不受限制:链表的大小不受限制,可以根据实际需要进行扩展。

2. 链表的缺点:

  1. 随机访问低效:由于链表中的元素不是按顺序存储的,因此随机访问某个元素的效率比较低,需要从头开始遍历O(N)
  2. 存储空间开销大:链表每个节点都需要额外的指针来指向下一个节点,这样会增加存储空间的开销。
  3. 缓存不友好:由于链表中的元素不是按顺序存储的,因此可能会导致缓存未命中,降低访问效率。

2. 顺序表

2.1顺序表的优点

  1. 随机访问高效:下标的随机访问效率高O(1)
  2. 尾删尾插高效:顺序表的尾插尾删不需要移动数据,效率高。
  3. 缓存友好:由于链表中的元素是按顺序存储的,缓存命中率高,访问效率高。

2.2顺序表的缺点

  1. 部分插入删除操作低效:由于顺序表中的元素是物理结构上是连续的,当数据插入删除前面顺序表的时候需要移动数据,效率低O(N)
  2. 内存大小受限:当顺序表内存存满后需要扩容,扩容需要代价,并且顺序表通常会有内存的浪费情况。

结尾

如果有什么建议和疑问,或是有什么错误,希望大家能够提一下。
希望大家以后也能和我一起进步!!
如果这篇文章对你有用的话,希望能给我一个小小的赞!

相关文章:

【数据结构】线性表之链表

目录 前言一、链表的定义二、链表的分类1. 单向和双向2. 带头和不带头3. 循环和不循环4. 常用&#xff08;无头单向非循环链表和带头双向循环链表&#xff09; 三、无头单向非循环链表的接口及实现1. 单链表的接口2. 接口的实现 四、带头双向循环链表接口的及实现1. 双向链表的…...

微服架构基础设施环境平台搭建 -(四)在Kubernetes集群基础上搭建Kubesphere平台

微服架构基础设施环境平台搭建 -&#xff08;四&#xff09;在Kubernetes集群基础上搭建Kubesphere平台 通过采用微服相关架构构建一套以KubernetesDocker为自动化运维基础平台&#xff0c;以微服务为服务中心&#xff0c;在此基础之上构建业务中台&#xff0c;并通过Jekins自动…...

Linux开发板安装Python环境

1. 环境介绍 硬件&#xff1a;STM32MP157&#xff0c;使用的是野火出的开发板。 软件&#xff1a;Debian ARM 架构制作的 Linux 发行版&#xff0c;版本信息如下&#xff1a; Linux发行版本&#xff1a;Debian GNU/Linux 10 内核版本&#xff1a;4.19.94 2. Python 简介…...

ChatGPT 聊天接口API 使用

一、准备工作 1.准备 OPENAI_ACCESS_TOKEN 2.准备好PostMan 软件 二、测试交流Demo 本次使用POSTMAN工具进行快速测试&#xff0c;旨在通过ChatGPT API实现有效的上下文流。在测试过程中&#xff0c;我们发现了三个问题&#xff1a;    1.如果您想要进行具有上下文的交流&…...

软件测试月薪2万,需要技术达到什么水平?

最近跟朋友在一起聚会的时候&#xff0c;提了一个问题&#xff0c;说一个软件测试工程师如何能月薪达到二万&#xff0c;技术水平需要达到什么程度&#xff1f;人回答说这只能是大企业或者互联网企业工程师才能拿到。也许是的&#xff0c;小公司或者非互联网企业拿二万的不太可…...

从入门到进阶,Vue框架让Web开发更简单高效

Vue是现代前端开发中最为流行的JavaScript框架之一&#xff0c;它具有轻量、易学、易用的特点&#xff0c;能够帮助开发者构建出高效、交互丰富的Web应用。在本文中&#xff0c;我们将会深入探索Vue框架的各个方面&#xff0c;包括Vue组件、Vue路由、Vue状态管理等&#xff0c;…...

怎么缩小照片的kb,压缩照片kb的几种方法

缩小照片的KB大小是我们日常工作生活中遇到的常见问题。虽然听起来十分专业&#xff0c;但其实很简单。照片的KB是指照片文件的大小&#xff0c;通常以“KB”为单位表示。缩小照片的KB就是减小照片文件的大小&#xff0c;以便占用更少的磁盘空间或更快地上传和下载照片。在实际…...

2. 注解Annotation

Java注解(Annotation)又称为Java标注,是JDK5.0引入的一种注释机制.注解是原数据的一种形式,提供有关于程序但不属于程序本身的数据.注解对他们注解的代码的操作没有直接的影响. 声明方式 注解的声明方式使用interface关键字,举例说明: public interface MyInject{ }元注解 Ta…...

【Leetcode -495.提莫攻击 -496.下一个更大的元素Ⅰ】

Leetcode Leetcode -495.提莫攻击Leetcode - 496.下一个更大的元素Ⅰ Leetcode -495.提莫攻击 题目&#xff1a;在《英雄联盟》的世界中&#xff0c;有一个叫 “提莫” 的英雄。他的攻击可以让敌方英雄艾希&#xff08;编者注&#xff1a;寒冰射手&#xff09;进入中毒状态。 …...

肝一肝设计模式【八】-- 外观模式

系列文章目录 肝一肝设计模式【一】-- 单例模式 传送门 肝一肝设计模式【二】-- 工厂模式 传送门 肝一肝设计模式【三】-- 原型模式 传送门 肝一肝设计模式【四】-- 建造者模式 传送门 肝一肝设计模式【五】-- 适配器模式 传送门 肝一肝设计模式【六】-- 装饰器模式 传送门 肝…...

Maven uber-jar(带依赖的打包插件)maven-shade-plugin

文章目录 最基础的 maven-shade-plugin 使用生成可执行的 Jar 包 和 常用的资源转换类包名重命名打包时排除依赖与其他常用打包插件比较 本文是对 maven-shade-plugin 常用配置的介绍&#xff0c;更详细的学习请参照 Apache Maven Shade Plugin 官方文档 通过使用 maven-shade…...

MySQL基础(二十八)索引优化与查询优化

都有哪些维度可以进行数据库调优?简言之: 索引失效、没有充分利用到索引——索引建立关联查询太多JOIN (设计缺陷或不得已的需求)——SQL优化服务器调优及各个参数设置(缓冲、线程数等)———调整my.cnf。数据过多――分库分表 关于数据库调优的知识点非常分散。不同的DBMS&…...

初步认识性能测试和完成一次完整的性能测试

上一篇博文主要通过两个例子让测试新手了解一下测试思想&#xff0c;和在做测试之前应该了解人几点&#xff0c;那么我们在如何完成一次完整的性能测试呢&#xff1f; 测试报告是一次完整性能测试的体现&#xff0c;所以&#xff0c;这里我给出一个完整的性能测试报告&#xff…...

使用插件快速生成代码

使用插件快速生成代码 咋们常说&#xff0c;授人以鱼不如授人以渔&#xff0c;在这里给大家提供一些技巧性的东西&#xff0c;方便一些新手同学可以快速上手&#xff0c;同时&#xff0c;也提高我们的开发兴趣与开发热情&#xff01; 主要讲什么呢&#xff0c;我们来学一学如何…...

FE_Vue学习笔记 插槽 slot

插槽分为匿名插槽、具名插槽、作用域插槽。子组件中&#xff1a; 匿名插槽只能有一个&#xff1b;可以有多个具名插槽&#xff1b;作用域插槽中可以有匿名插槽和具名插槽。 当项目中一个组件可以多次复用时&#xff0c;我们可以把这个组件封装成单独的.vue文件&#xff0c;从…...

单链表的成环问题

前言&#xff1a;链表成环问题不仅考察双指针的用法&#xff0c;该问题还需要一定的数学推理和分析能力&#xff0c;看似简单的题目实则细思缜密&#xff0c;值得斟酌~ 目录 1.问题背景引入-判断链表是否成环&#xff1a; 1.1.正解&#xff1a;快慢指针 1.2 STL的集合判重 …...

横截面收益率

横截面收益率指的是在经典资产定价模型中&#xff0c;在横截面上线性确定的一个与资产风险匹配的资产收益率。 横截面收益率的预测[1] (一)变量和方法 我们主要使用月度频率数据进行检验。交易数据和公司财务数据来自于CSMAR数据库。CSMAR数据库的收益率调整了送股、配股以及拆…...

C++解析JSON JSONCPP库的使用

首先去GitHub下载JSONCPP的源码&#xff1a; JSonCpp的源码 解压后得到&#xff1a;jsoncpp-master 文件夹 需要的是&#xff1a;jsoncpp-master\src\lib_json 目录下的所有文件和 jsoncpp-master\include\json 目录下的所有文件&#xff0c;在MFC工程目录下新建两个文件夹或…...

不会Elasticsearch标准查询语句,如何分析数仓数据?

1 Elasticsearch的查询语句 ES中提供了一种强大的检索数据方式,这种检索方式称之为Query DSL,Query DSL是利用Rest API传递JSON格式的请求体(Request Body)数据与ES进行交互&#xff0c;这种方式的丰富查询语法让ES检索变得更强大&#xff0c;更简洁。 1.1 查询预发 # GET /…...

获得GitHub Copilot并结合VS Code使用

一、什么是GitHub Copilot GitHub Copilot是一种基于AI的代码生成工具。它使用OpenAI的GPT&#xff08;生成式预训练Transformer&#xff09;技术来提供建议。它可以根据您正在编写的代码上下文建议代码片段甚至整个函数。 要使用GitHub Copilot&#xff0c;您需要在编辑器中…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...