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

从零开始学习数据结构—【链表】—【探索环形链的设计之美】

环形链表

文章目录

  • 环形链表
    • 1.结构图
    • 2.具体实现
      • 2.1.环形链表结构
      • 2.2.头部添加数据
        • 2.2.1.具体实现
        • 2.2.2.测试添加数据
      • 2.3.尾部添加数据
        • 2.3.1.具体实现
        • 2.3.2.添加测试数据
      • 2.4.删除头部数据
        • 2.4.1.具体实现
        • 2.4.2.测试删除数据
      • 2.5.删除尾部数据
        • 2.5.1.具体实现
        • 2.5.2.测试删除数据
      • 2.6.根据内容删除节点
        • 2.6.1.具体实现
      • 2.7.遍历环形链表
        • 2.7.1.迭代器遍历
        • 2.7.2.使用递归进行遍历
      • 2.7.3.测试
    • 3.具体应用场景
      • 3.1.优点
      • 3.2.缺点
      • 3.3.应用场景

1.结构图

双向环形链表带哨兵,这个时候的哨兵可以当头,也可做尾

带哨兵双向循环链表:结构稍微复杂,实现简单。一般用来单独存储数据,实际中使用的链表数据结构都是带头双向链表。另外,这个结构虽然结构复杂,但是使用代码实现后会发现结构会带来很多优势。

双向环形链表是一种链式数据结构,其每个节点包含指向前一个节点和后一个节点的指针,形成了一个闭环。这意味着链表的尾部节点指向头部节点,而头部节点指向尾部节点,形成了一个环状的结构。

带哨兵的双向环形链表在头部和尾部都有一个特殊的哨兵节点,这个哨兵节点不存储任何数据,仅用于简化链表的操作。哨兵节点使得链表中始终存在一个不变的头部和尾部,即使链表为空也如此。具体而言:

  1. 头部哨兵节点: 位于链表的头部,它的前驱节点指向链表的尾部节点,而它的后继节点指向链表的第一个真实节点。头部哨兵节点使得在头部执行操作时变得更加简单,不需要特殊处理链表为空的情况,也不需要区分头部和尾部的操作。
  2. 尾部哨兵节点: 位于链表的尾部,它的后继节点指向链表的头部节点,而它的前驱节点指向链表的最后一个真实节点。尾部哨兵节点同样简化了尾部操作,使得在尾部进行插入、删除等操作更加方便。

带哨兵的双向环形链表在实现时通常会带来一些优势:

  • 简化操作: 哨兵节点的存在使得对链表头部和尾部的操作变得更加统一和简化。不需要特别处理头部或尾部为空的情况,使得代码更加清晰和简洁。
  • 增强鲁棒性: 哨兵节点可以避免出现空指针异常,因为链表中始终存在一个不变的头部和尾部。这增强了代码的鲁棒性和可靠性。
  • 逻辑统一: 哨兵节点的存在使得链表的逻辑更加统一,不需要在特殊情况下单独处理头部或尾部节点,使得代码更加一致性和可读性。

在这里插入图片描述

2.具体实现

2.1.环形链表结构

public class DoubleLinkedListSentinel {private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());public DoubleLinkedListSentinel(){// 初始化时 环形连链表创建指向自身sentinel.next = sentinel;sentinel.prev = sentinel;}/*** 创建哨兵*/private Node sentinel = new Node(null,-1,null);private static class Node{Node prev;     // 头指针Integer value; // 值Node next;     // 尾指针public Node(Node prev, Integer value, Node next) {this.prev = prev;this.value = value;this.next = next;}}/*** 重写toString 用于Json输出* @return*/@Overridepublic String toString() {StringBuffer sb = new StringBuffer();Node p = sentinel.next;while (p != sentinel){sb.append(","+p.value);p = p.next;}//  StringUtils.strip(sb.toString() 去除首位固定字符return "Node[ " + StringUtils.strip(sb.toString(), ",") +" ]";}
}

2.2.头部添加数据

# 思路找到哨兵,找到哨兵的下一个节点,创建新的对象(指定新节点的前后节点),将哨兵next指向新创建的节点,将哨兵的下一个节点指向新加的节点

在这里插入图片描述

2.2.1.具体实现
	/*** 在头部添加值* @param value 待添加的元素*/public void addFirst(int value){// 找到哨兵Node head = sentinel;// 找到哨兵的下一个节点Node next = sentinel.next;// 创建新的对象Node addNode = new Node(head, value, next);// 将哨兵next指向新创建的节点head.next = addNode;//将哨兵的下一个节点的头节点指向新加的节点next.prev = addNode;}
2.2.2.测试添加数据
@Test
@DisplayName("测试双向环形链表")
public void test(){DoubleLinkedListSentinel node = new DoubleLinkedListSentinel();logger.error("add after node :{}",node);node.addFirst(1);node.addFirst(2);node.addFirst(3);logger.error("add after node :{}",node);
}

在这里插入图片描述

2.3.尾部添加数据

# 思路找到最后一个节点,找到头节点,创建新的节点(指明前后节点),将最后一个节点的next指向新创建的节点,将头节点的prev指向新创建的节点

在这里插入图片描述

2.3.1.具体实现
/*** 向链表的最后一个节点添元素* @param value 需要添加元素的值*/
public void addLast(int value){// 找到最后一个节点Node next = sentinel.prev;// 找到头节点Node head = sentinel;// 创建新的节点Node node = new Node(next, value, head);// 将最后一个节点的next指向新创建的节点next.next = node;// 将头节点的prev指向新创建的节点head.prev = node;
}
2.3.2.添加测试数据
	@Test@DisplayName("测试-双向环形链表-尾部添加元素")public void tes2(){DoubleLinkedListSentinel node = new DoubleLinkedListSentinel();node.addLast(1);node.addLast(2);node.addLast(3);node.addLast(4);logger.error("linked list is: :{}",node);}

在这里插入图片描述

2.4.删除头部数据

# 思路 找到需要删除的节点,找到上一个节点,找到删除节点的下一个节点,将头节点的next指向删除节点的下一个节点,将删除节点的prev指向head

在这里插入图片描述

2.4.1.具体实现
/*** 删除第一个节点*/
public void removedFirst(){// 先找到需要删除的节点Node deleteNode = sentinel.next;// 如果删除的节点等于哨兵 那么不能删除if (deleteNode == sentinel){throw new IllegalArgumentException("delete node is null!");}// 找到上一个节点Node head = sentinel;// 找到删除节点的下一个节点Node next = deleteNode.next;// 将头节点的next指向删除节点的下一个节点head.next = next;// 将删除节点的prev指向headnext.prev = head;
}
2.4.2.测试删除数据
@Test
@DisplayName("测试-双向环形链表-删除第一个数据")
public void tes2(){DoubleLinkedListSentinel node = new DoubleLinkedListSentinel();node.addLast(1);node.addLast(2);node.addLast(3);node.addLast(4);logger.error("remove first node :{},size :{}",node,node.size());logger.error("------------------ remove ----------------");node.removedFirst();logger.error("remove first node :{},size :{}",node,node.size());node.removedFirst();logger.error("remove first node :{},size :{}",node,node.size());node.removedFirst();logger.error("remove first node :{},size :{}",node,node.size());node.removedFirst();logger.error("remove first node :{},size :{}",node,node.size());node.removedFirst();
}

在这里插入图片描述

2.5.删除尾部数据

# 思路找到最后一个节点,找到删除节点的上一个节点,找到删除节点的下一个节点,将删除节点的上一个节点 next指向头部,将哨兵执行最后一个节点

在这里插入图片描述

2.5.1.具体实现
/*** 删除最后一个节点*/public void removeLast(){// 找到最后一个节点Node deleteNode = sentinel.prev;// 如果删除的节点等于哨兵 那么不能删除if (deleteNode == sentinel){throw new IllegalArgumentException("delete node is null!");}// 找到删除节点的上一个节点Node head = deleteNode.prev;// 将删除节点的下一个节点Node next = sentinel;// 将删除的节点的上一个节点 next指向头部head.next = next;// 将哨兵执行最后一个节点next.prev = head;}
2.5.2.测试删除数据
@Test
@DisplayName("测试-双向环形链表-删除最后一个数据")
public void tes2(){DoubleLinkedListSentinel node = new DoubleLinkedListSentinel();node.addLast(1);node.addLast(2);node.addLast(3);node.addLast(4);logger.error("remove last node :{},size :{}",node,node.size());logger.error("------------------ remove ----------------");node.removeLast();logger.error("remove last node :{},size :{}",node,node.size());node.removeLast();logger.error("remove last node :{},size :{}",node,node.size());node.removeLast();logger.error("remove last node :{},size :{}",node,node.size());node.removeLast();logger.error("remove last node :{},size :{}",node,node.size());node.removeLast();
}

在这里插入图片描述

2.6.根据内容删除节点

# 思路找到需要删除的节点,找到删除节点的上一个节点,找到删除节点的下一个节点,将删除节点的上一个节点 next指向删除节点的下一个节点,将删除节点的下一个节点 prev指向删除节点的上一个节点

在这里插入图片描述

2.6.1.具体实现
@Test
@DisplayName("测试-双向环形链表-根据内容删除元素")
public void tes2(){DoubleLinkedListSentinel node = new DoubleLinkedListSentinel();node.addLast(1);node.addLast(2);node.addLast(3);node.addLast(4);int r1 = RandomUtils.nextInt(1, 5);int r2 = RandomUtils.nextInt(1, 10);logger.error("linked list :{}",node);int i = node.removeByIndex(r1);if (i == -1){logger.error("未找到需要删除的元素,{}",r1);}else {logger.error("删除成功,{}",r1);}int j = node.removeByIndex(r2);if (j == -1){logger.error("未找到需要删除的元素,{}",r2);}else {logger.error("删除成功,{}",r2);}logger.error("find linked list :{}",node);
}

在这里插入图片描述

2.7.遍历环形链表

2.7.1.迭代器遍历
// 实现 public class DoubleLinkedListSentinel implements Iterable<Integer> 接口  重写/*** 通过实现迭代器 进行循环遍历*/
@Override
public Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p = sentinel.next;@Overridepublic boolean hasNext() {return p != sentinel;}@Overridepublic Integer next() {Integer value = p.value;p = p.next;return value;}};
}
2.7.2.使用递归进行遍历
/*** 递归遍历(递归遍历)*/
public void loop(Consumer<Integer> before,Consumer<Integer> after){recursion(sentinel.next,before,after);
}/*** 递归进行遍历* @param node   下一个节点* @param before 遍历前执行的方法* @param after  遍历后执行的方法* @deprecated  递归遍历,不建议使用,递归深度过大会导致栈溢出。建议使用迭代器,或者循环遍历,或者使用尾递归,或者使用栈* @see #loop(Consumer, Consumer)*/
public void recursion(Node node, Consumer<Integer> before, Consumer<Integer> after){// 表示链表没有节点了,那么就退出(注意 环形链表的 末尾 不是null 而是头节点)if (node == sentinel){return;}// 反转位置就是逆序了before.accept(node.value);recursion(node.next, before, after);after.accept(node.value);
}

2.7.3.测试

	@Test@DisplayName("测试-双向环形链表-遍历")public void tes3(){DoubleLinkedListSentinel node = new DoubleLinkedListSentinel();node.addLast(1);node.addLast(2);node.addLast(3);node.addLast(4);logger.error("=========== 迭代器遍历链表 ===========");for (Integer i : node) {logger.error("迭代器遍历链表 :{}",i);}logger.error("=========== 递归遍历链表 ===========");node.loop(it->{logger.error("从头部开始遍历 :{}",it);},it ->{logger.error("从尾部开始遍历 :{}",it);});}

在这里插入图片描述

3.具体应用场景

3.1.优点

  1. 循环遍历简便: 由于双向环形链表形成了一个闭环,因此在需要循环遍历链表时,可以更加简便地实现,不需要额外的指针变量来记录链表的尾部或头部。
  2. 高效的插入和删除操作: 双向环形链表的节点结构允许在任意位置进行节点的插入和删除操作,并且这些操作通常比较高效,尤其是在头部和尾部进行操作时。
  3. 适用于循环结构数据: 对于需要处理循环结构的数据或需要实现环形队列等特定功能的场景,双向环形链表是一种很自然的数据结构选择。

3.2.缺点

  1. 强引用导致的内存泄漏: 如果双向环形链表中的节点持有对外部对象的强引用,并且这些外部对象的生命周期比链表更长,那么即使链表中的节点不再被使用,这些节点仍然被链表中的引用所持有,从而无法被垃圾回收器回收,导致内存泄漏。
  2. 未正确处理节点的引用关系: 在双向环形链表中,节点之间相互引用,如果在节点删除或者替换的过程中未正确地处理节点之间的引用关系,可能会导致链表中的节点无法被回收,从而引发内存泄漏。
  3. 长期持有迭代器: 如果在遍历双向环形链表的过程中长期持有迭代器对象,而没有正确地释放迭代器对象,可能会导致链表中的节点无法被回收,造成内存泄漏。
  4. 容易产生死循环: 由于环形链表的特性,编写循环遍历的代码时需要特别小心,如果没有正确地处理循环结束的条件,可能会产生死循环,导致程序崩溃或陷入无限循环。
  5. 实现复杂度较高: 相比于普通的单向链表,双向环形链表的实现复杂度较高,需要更多的代码来维护节点之间的引用关系,尤其是在节点的插入和删除操作时需要考虑更多的边界条件。

3.3.应用场景

  1. LRU Cache(最近最少使用缓存): 在LRU缓存中,双向环形链表可以用于维护最近使用的数据项的顺序。每次访问缓存中的数据时,可以将该数据项移到链表的头部,而最少使用的数据项则会被移动到链表的尾部,当缓存空间不足时,可以删除链表尾部的数据项。双向环形链表使得在链表头尾进行插入和删除操作更加高效。
  2. 循环队列: 在某些情况下,需要实现循环队列以存储和处理数据,比如在生产者-消费者模型中。双向环形链表可以用作循环队列的基础数据结构,使得在队列头尾进行数据插入和删除操作更加高效。
  3. 哈希表的冲突解决: 在哈希表中,如果多个键散列到相同的槽位上,就会发生冲突。双向环形链表可以用作哈希表中槽位的链表,用于解决冲突,实现链地址法(Separate Chaining)的哈希表。

相关文章:

从零开始学习数据结构—【链表】—【探索环形链的设计之美】

环形链表 文章目录 环形链表1.结构图2.具体实现2.1.环形链表结构2.2.头部添加数据2.2.1.具体实现2.2.2.测试添加数据 2.3.尾部添加数据2.3.1.具体实现2.3.2.添加测试数据 2.4.删除头部数据2.4.1.具体实现2.4.2.测试删除数据 2.5.删除尾部数据2.5.1.具体实现2.5.2.测试删除数据 …...

AJAX——HTTP协议

1 HTTP协议-请求报文 HTTP协议&#xff1a;规定了浏览器发送及服务器返回内容的格式 请求报文&#xff1a;浏览器按照HTTP协议要求的格式&#xff0c;发送给服务器的内容 1.1 请求报文的格式 请求报文的组成部分有&#xff1a; 请求行&#xff1a;请求方法&#xff0c;URL…...

java面试微服务篇

目录 目录 SpringCloud Spring Cloud 的5大组件 服务注册 Eureka Nacos Eureka和Nacos的对比 负载均衡 负载均衡流程 Ribbon负载均衡策略 自定义负载均衡策略 熔断、降级 服务雪崩 服务降级 服务熔断 服务监控 为什么需要监控 服务监控的组件 skywalking 业务…...

JS进阶——垃圾回收机制以及算法

版权声明 本文章来源于B站上的某马课程&#xff0c;由本人整理&#xff0c;仅供学习交流使用。如涉及侵权问题&#xff0c;请立即与本人联系&#xff0c;本人将积极配合删除相关内容。感谢理解和支持&#xff0c;本人致力于维护原创作品的权益&#xff0c;共同营造一个尊重知识…...

【快速解决】python项目打包成exe文件——vscode软件

目录 操作步骤 1、打开VSCode并打开你的Python项目。 2、在VSCode终端中安装pyinstaller&#xff1a; 3、运行以下命令使用pyinstaller将Python项目打包成exe文件&#xff1a; 其中your_script.py是你的Python脚本的文件名。 4、打包完成后&#xff0c;在你的项目目录中会…...

数据结构——lesson3单链表介绍及实现

目录 1.什么是链表&#xff1f; 2.链表的分类 &#xff08;1&#xff09;无头单向非循环链表&#xff1a; &#xff08;2&#xff09;带头双向循环链表&#xff1a; 3.单链表的实现 &#xff08;1&#xff09;单链表的定义 &#xff08;2&#xff09;动态创建节点 &#…...

中科大计网学习记录笔记(八):FTP | EMail

前言&#xff1a; 学习视频&#xff1a;中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版&#xff0c;James F.Kurose&#xff0c;Keith W.Ross&#xff09;》课程 该视频是B站非常著名的计网学习视频&#xff0c;但相信很多朋友和我一样在听完前面的部分发现信…...

QPaint绘制自定义坐标轴组件00

最终效果 1.创建一个ui页面&#xff0c;修改背景颜色 鼠标右键->改变样式表->添加颜色->background-color->选择合适的颜色->ok->Apply->ok 重新运行就可以看到widget的背景颜色已经改好 2.创建一个自定义的widget窗口小部件类&#xff0c;class MyChart…...

MATLAB|基于改进二进制粒子群算法的含需求响应机组组合问题研究(含文献和源码)

目录 主要内容 模型研究 1.改进二进制粒子群算法&#xff08;BPSO&#xff09; 2.模型分析 结果一览 下载链接 主要内容 该程序复现《A Modified Binary PSO to solve the Thermal Unit Commitment Problem》&#xff0c;主要做的是一个考虑需求响应的机组组合…...

JDBC核心技术

第1章 JDBC概述 第2章 获取数据库连接 第3章 使用PreparedStatement实现CRUD操作 第4章 操作BLOB类型字段 第5章 批量插入 第6章 数据库事务 第7章 DAO及相关实现类 第8章 数据库连接池 第9章 Apache-DBUtils实现CRUD操作图像 小部件...

【天幕系列 02】开源力量:揭示开源软件如何成为技术演进与社会发展的引擎

文章目录 导言01 开源软件如何推动技术创新1.1 开放的创新模式1.2 快速迭代和反馈循环1.3 共享知识和资源1.4 生态系统的建设和扩展1.5 开放标准和互操作性 02 开源软件的商业模式2.1 支持和服务模式2.2 基于订阅的模式2.3 专有附加组件模式2.4 开源软件作为平台模式2.5 双重许…...

“挖矿”系列:细说Python、conda 和 pip 之间的关系

继续挖矿&#xff0c;挖“金矿”&#xff01; 1. Python、conda 和 pip&#xff08;挖“金矿”工具&#xff09; Python、conda 和 pip 是在现代数据科学和软件开发中常用的工具&#xff0c;它们各自有不同的作用&#xff0c;但相互之间存在密切的关系&#xff1a; Python&…...

【自然语言处理】实验3,文本情感分析

清华大学驭风计划课程链接 学堂在线 - 精品在线课程学习平台 (xuetangx.com) 代码和报告均为本人自己实现&#xff08;实验满分&#xff09;&#xff0c;只展示主要任务实验结果&#xff0c;如果需要详细的实验报告或者代码可以私聊博主 有任何疑问或者问题&#xff0c;也欢…...

2.12日学习打卡----初学RocketMQ(三)

2.12日学习打卡 目录&#xff1a; 2.12日学习打卡一. RocketMQ高级特性&#xff08;续&#xff09;消息重试延迟消息消息查询 二.RocketMQ应用实战生产端发送同步消息发送异步消息单向发送消息顺序发送消息消费顺序消息全局顺序消息延迟消息事务消息消息查询 一. RocketMQ高级特…...

<网络安全>《35 网络攻防专业课<第一课 - 网络攻防准备>》

1 主要内容 认识黑客 认识端口 常见术语与命令 网络攻击流程 VMWare虚拟环境靶机搭建 2 认识黑客 2.1 白帽、灰帽和黑帽黑客 白帽黑客是指有能力破坏电脑安全但不具恶意目的黑客。 灰帽黑客是指对于伦理和法律态度不明的黑客。 黑帽黑客经常用于区别于一般&#xff08;正面…...

【实战】一、Jest 前端自动化测试框架基础入门(一) —— 前端要学的测试课 从Jest入门到TDD BDD双实战(一)

文章目录 一、前端要学的测试课1.前端要学的测试2.前端工程化的一部分3.前端自动化测试的例子4.前端为什么需要自动化测试&#xff1f;5.课程涵盖内容6.前置技能7.学习收获 二、Jest 前端自动化测试框架基础入门1. 自动化测试背景及原理前端自动化测试产生的背景及原理 2.前端自…...

蓝桥杯Java组备赛(二)

题目1 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int max Integer.MIN_VALUE;int min Integer.MAX_VALUE;double sum 0;for(int i0;i<n;i) {int x sc.nextInt()…...

人力资源智能化管理项目(day10:首页开发以及上线部署)

学习源码可以看我的个人前端学习笔记 (github.com):qdxzw/humanResourceIntelligentManagementProject 首页-基本结构和数字滚动 安装插件 npm i vue-count-to <template><div class"dashboard"><div class"container"><!-- 左侧内…...

Conda管理Python不同版本教程

Conda管理Python不同版本教程 目录 0.前提 1.conda常用命令 2.conda设置国内源&#xff08;以添加清华源为例&#xff0c;阿里云源同样&#xff09; 3.conda管理python库 4.其它 不太推荐 pyenv管理Python不同版本教程&#xff08;本人另一篇博客&#xff0c;姊妹篇&…...

free pascal:fpwebview 组件通过 JSBridge 调用本机TTS

从 https://github.com/PierceNg/fpwebview 下载 fpwebview-master.zip 简单易用。 先请看 \fpwebview-master\README.md cd \lazarus\projects\fpwebview-master\demo\js_bidir 学习 js_bidir.lpr &#xff0c;编写 js_bind_speak.lpr 如下&#xff0c;通过 JSBridge 调用本…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...