数据结构与算法——Java实现 19.队列

目录

一、概述

二、链表实现队列

接口定义

 接口实现类

测试类 

三、环形数组实现队列

优点

下标计算

判满和判空

判满

判空

辅助变量size判空和判满

方法1

 接口定义

接口实现类

测试类 

方式2

接口定义

接口实现类

测试类

方法3

 接口定义

接口实现类

测试类 


生活鲜少给人留下退路

                        —— 24.9.27

一、概述

计算机科学中,queue 是以顺序的方式维护的一组数据集合,在一端添加数据,从另一端移除数据。习惯来说,添加的一端称为尾,移除的一端称为头,就如同生活中的排队买商品

二、链表实现队列

下面以单向环形带哨兵链表方式来实现队列

接口定义

offer:向队列尾部插入值

poll:从队列头部获取值,并移除

peek:从队列头获取值,不移除

isEmpty:检查队列是否为空

isFull:检查队列是否为满

public interface Queue<E> {/*向队列尾部插入值Params:value-待插入值Returns:插入成功返回true,插入失败返回false*/boolean offer(E e);/*从队列头获取值,并移除Returns:如果队列非空返回头部值,否则返回null*/E poll();/*从队列头获取值,不移除Returns:如果队列非空返回队头值,否则返回null*/E peek();/*检查队列是否为空Return:空返回true,否则返回false*/boolean isEmpty();/*检查队列是否为满Return:满返回true,否则返回false*/boolean isFull();
}

 接口实现类

import java.util.Iterator;// 用泛型好处:1.用null代表特殊值   2.代表更多类型
public class LinkedListQueue<E>implements Queue<E>,Iterable<E> {// 静态内部结点类private static class Node<E>{E value;Node<E> next;public Node(E value, Node<E> next){this.value = value;this.next = next;}}// 定义队列的头结点和尾节点Node<E> head = new Node<>(null,null);Node<E> tail = head;private int size = 0; // 当前节点数private int capacity = 10; // 队列最大容量public LinkedListQueue(int capacity) {this.capacity = capacity;tail.next = head;}public LinkedListQueue() {tail.next = head;}/*队列插入方法,在尾部插入Params:value-待插入值Returns:插入成功返回true,插入失败返回false*/@Overridepublic boolean offer(E e) {if(isFull()){return false;}Node<E> newNode = new Node<>(e,head);tail.next = newNode;tail = newNode;size++;return true;}/*从队头获取值,并移除Returns:如果队列非空返回队头值,否则返回null*/@Overridepublic E poll() {if (isEmpty()){return null;}Node<E> first = head.next;head.next = first.next;if (first == tail){tail = head;}size--;return first.value;}/*从队列头获取值,不移除Returns:如果队列非空返回队头值,否则返回null*/@Overridepublic E peek() {if(isEmpty()){return null;}return head.next.value;}/*检查队列是否为空Return:空返回true,否则返回false*/@Overridepublic boolean isEmpty() {return head == tail;}/*检查队列是否为满Return:满返回true,否则返回false*/@Overridepublic boolean isFull() {return size == capacity;}// 队列遍历方法 迭代器@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {Node<E> current = head.next;@Overridepublic boolean hasNext() {return current != head;}@Overridepublic E next() {E value = current.value;current = current.next;return value;}};}
}

测试类 

import org.junit.Test;import java.util.List;import static org.junit.jupiter.api.Assertions.*;public class TestLinkedListQueue {// 向队列尾部插入值@Testpublic void offer() {LinkedListQueue<Integer> queue = new LinkedListQueue<>();queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertIterableEquals(queue,List.of(1,2,3,4,5));}// 从队列头获取值,不移除@Testpublic void peek(){LinkedListQueue<Integer> queue = new LinkedListQueue<>();assertNull(queue.peek());queue.offer(1);assertEquals(1,queue.peek());queue.offer(2);assertEquals(1,queue.peek());queue.offer(3);assertEquals(1,queue.peek());}// 从队头获取值,并移除@Testpublic void poll() {LinkedListQueue<Integer> queue = new LinkedListQueue<>();queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertEquals(1,queue.poll());assertEquals(2,queue.poll());assertEquals(3,queue.poll());assertEquals(4,queue.poll());assertEquals(5,queue.poll());assertNull(queue.poll());}// 向有限队列头部加入元素@Testpublic void offerLimit(){LinkedListQueue<Integer> queue = new LinkedListQueue<>(3);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertIterableEquals(queue,List.of(1,2,3));}}

三、环形数组实现队列

环形数组:

        大小固定,首尾相连的数组

        环形数组是一种逻辑上形成环形结构的数组,它在物理上通常是一个普通的线性数组,但在访问和操作时采用特定的逻辑来处理边界条件,使得元素可以从数组的末尾“循环”到开头,或者从开头“循环”到末尾。这种结构常用于实现循环队列、滑动窗口和约瑟夫环等问题,因为它避免了传统数组的越界问题,并且具有空间效率高、适合FIFO(先进先出)操作等优点。

环形数组的特点

① 逻辑环形:环形数组在逻辑上形成一个闭环,数组的最后一个元素与第一个元素相连。

② 无边界问题:由于索引是循环的,因此不存在传统数组的越界问题。

③ 空间效率高:环形数组可以充分利用数组空间,避免不必要的空间浪费。

④ 适合特定操作:如循环队列、滑动窗口等,这些操作在环形数组上实现起来更加高效。

环形数组的应用

① 循环队列:队列是一种先进先出(FIFO)的数据结构,而循环队列则是使用环形数组来实现的一种队列。在循环队列中,当队列的尾部达到数组的末尾时,下一个元素将插入到数组的开头,从而形成一个循环。

② 滑动窗口:滑动窗口是一种常用于数组/字符串处理的算法技巧,它可以在线性时间内解决一些看似需要嵌套循环的问题。环形数组可以用于实现滑动窗口的某些变体,特别是当窗口大小固定且需要循环移动时。

③ 约瑟夫环问题:约瑟夫环问题是一个著名的数学问题,描述的是n个人围成一圈,从第一个人开始报数,报到m的人出圈,然后从下一个人重新开始报数,如此循环直到所有人出圈。环形数组可以很好地模拟这个问题中的环形结构。

优点

        1.对比普通数组,起点和终点更为自由,不用考虑数据移动

        2.“环“意味着不会存在【越界】问题

        3.数组性能更佳

        4.环形数组比较适合实现有界队列、RingBuffer 等

下标计算

索引位置 =  (cur + step) % length

cur:当前指针位置

step:前进步数

length:数组长度

判满和判空

判满

(尾指针+1)% 数组长度 = 头指针

判空

头指针 = 尾指针

辅助变量size判空和判满

方法1

使用头尾指针判断队列空和满的情况

 接口定义

public interface Queue<E> {/*向队列尾部插入值Params:value-待插入值Returns:插入成功返回true,插入失败返回false*/boolean offer(E e);/*从队列头获取值,并移除Returns:如果队列非空返回头部值,否则返回null*/E poll();/*从队列头获取值,不移除Returns:如果队列非空返回队头值,否则返回null*/E peek();/*检查队列是否为空Return:空返回true,否则返回false*/boolean isEmpty();/*检查队列是否为满Return:满返回true,否则返回false*/boolean isFull();
}

接口实现类

import java.util.Iterator;public class ArrayQueue1<E> implements Queue<E>,Iterable<E> {private final E[] array;private int head = 0;private int tail = 0;public ArrayQueue1(int capacity) {array = (E[])new Object[capacity+1];}// 从队列尾部加入元素@Overridepublic boolean offer(E e) {if (isFull()){return false;}array[tail] = e;tail = (tail + 1) % array.length;return true;}// 从队列头部移除元素@Overridepublic E poll() {if (isEmpty()){return null;}E value = array[head];head = (head + 1) % array.length;return value;}// 获取队列头部元素值@Overridepublic E peek() {if (isEmpty()){return null;}return array[head];}@Overridepublic boolean isEmpty() {return head == tail;}@Overridepublic boolean isFull() {return (tail+1) % array.length == head;}@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {int cursor = head;@Overridepublic boolean hasNext() {return cursor != tail;}@Overridepublic E next() {E value = array[cursor];cursor = (cursor + 1) % array.length;return value;}};}
}

测试类 

import org.junit.Test;import java.util.List;import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;public class TestArrayQueue1 {// 向队列尾部插入值@Testpublic void offer() {ArrayQueue1<Integer> queue = new ArrayQueue1<>(5);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertIterableEquals(queue, List.of(1,2,3,4,5));}// 从队列头获取值,不移除@Testpublic void peek(){ArrayQueue1<Integer> queue = new ArrayQueue1<>(5);assertNull(queue.peek());queue.offer(1);assertEquals(1,queue.peek());queue.offer(2);assertEquals(1,queue.peek());queue.offer(3);assertEquals(1,queue.peek());}// 从队头获取值,并移除@Testpublic void poll() {ArrayQueue1<Integer> queue = new ArrayQueue1<>(5);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertEquals(1,queue.poll());assertEquals(2,queue.poll());assertEquals(3,queue.poll());assertEquals(4,queue.poll());assertEquals(5,queue.poll());assertNull(queue.poll());}// 向有限队列头部加入元素@Testpublic void offerLimit(){ArrayQueue1<Integer> queue = new ArrayQueue1<>(3);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertIterableEquals(queue,List.of(1,2,3));}
}

方式2

引入一个辅助变量size,记录队列中的元素个数,直接通过用数组长度和size变量比较,判断队列空还是满。

接口定义

public interface Queue<E> {/*向队列尾部插入值Params:value-待插入值Returns:插入成功返回true,插入失败返回false*/boolean offer(E e);/*从队列头获取值,并移除Returns:如果队列非空返回头部值,否则返回null*/E poll();/*从队列头获取值,不移除Returns:如果队列非空返回队头值,否则返回null*/E peek();/*检查队列是否为空Return:空返回true,否则返回false*/boolean isEmpty();/*检查队列是否为满Return:满返回true,否则返回false*/boolean isFull();
}

接口实现类

import java.util.Iterator;public class ArrayQueue2<E> implements Queue<E>,Iterable<E> {private final E[] array;private int head = 0;private int tail = 0;private int size = 0;public ArrayQueue2(int capacity) {array = (E[]) new Object[capacity];}// 从队列尾部加入元素@Overridepublic boolean offer(E e) {if (isFull()){return false;}array[tail] = e;tail = (tail + 1) % array.length;size++;return true;}// 从队列头部移除元素@Overridepublic E poll() {if (isEmpty()){return null;}E value = array[head];head = (head + 1) % array.length;size--;return value;}// 获取队列头部元素值@Overridepublic E peek() {if (isEmpty()){return null;}return array[head];}@Overridepublic boolean isEmpty() {return size == 0;}@Overridepublic boolean isFull() {return size == array.length;}@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {int cursor = head;int count = 0;@Overridepublic boolean hasNext() {return count < size;}@Overridepublic E next() {E value = array[cursor];cursor = (cursor + 1) % array.length;count++;return value;}};}
}

测试类

import org.junit.Test;import java.util.List;import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;public class TestArrayQueue2 {// 向队列尾部插入值@Testpublic void offer() {ArrayQueue2<Integer> queue = new ArrayQueue2<>(5);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertIterableEquals(queue, List.of(1,2,3,4,5));}// 从队列头获取值,不移除@Testpublic void peek(){ArrayQueue2<Integer> queue = new ArrayQueue2<>(5);assertNull(queue.peek());queue.offer(1);assertEquals(1,queue.peek());queue.offer(2);assertEquals(1,queue.peek());queue.offer(3);assertEquals(1,queue.peek());}// 从队头获取值,并移除@Testpublic void poll() {ArrayQueue2<Integer> queue = new ArrayQueue2<>(5);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertEquals(1,queue.poll());assertEquals(2,queue.poll());assertEquals(3,queue.poll());assertEquals(4,queue.poll());assertEquals(5,queue.poll());assertNull(queue.poll());}// 向有限队列头部加入元素@Testpublic void offerLimit(){ArrayQueue2<Integer> queue = new ArrayQueue2<>(3);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertIterableEquals(queue,List.of(1,2,3));}
}

方法3

head和tail直接存储指针值,tail存储最后一个元素的指针值,计算tail存储指针值指向的索引,tail并不直接指向索引不把计算结果存入head和tail中

 接口定义

public interface Queue<E> {/*向队列尾部插入值Params:value-待插入值Returns:插入成功返回true,插入失败返回false*/boolean offer(E e);/*从队列头获取值,并移除Returns:如果队列非空返回头部值,否则返回null*/E poll();/*从队列头获取值,不移除Returns:如果队列非空返回队头值,否则返回null*/E peek();/*检查队列是否为空Return:空返回true,否则返回false*/boolean isEmpty();/*检查队列是否为满Return:满返回true,否则返回false*/boolean isFull();
}

接口实现类

import java.util.Iterator;public class ArrayQueue3<E> implements Queue<E>,Iterable<E> {private final E[] array;// head tail是两个不断递增的整数private int head = 0;private int tail = 0;@SuppressWarnings("all")public ArrayQueue3(int capacity) {// 初始化数组array = (E[]) new Object[capacity];}// 从队列尾部加入元素@Overridepublic boolean offer(E e) {// 判满if(isFull()){return false;}array[tail % array.length] = e;tail ++;return true;}// 从队列头部移除元素@Overridepublic E poll() {if (isEmpty()){return null;}// 找到索引位置E e = array[head % array.length];head ++;return e;}// 获取队列头部元素值@Overridepublic E peek() {if (isEmpty()){return null;}return array[head % array.length];}@Overridepublic boolean isEmpty() {return head == tail;}@Overridepublic boolean isFull() {return tail - head == array.length;}@Overridepublic Iterator<E> iterator() {return new Iterator<E>() {int cursor = head;@Overridepublic boolean hasNext() {return cursor != tail;}@Overridepublic E next() {E e = array[cursor % array.length];cursor++;return e;}};}
}

测试类 

import org.junit.Test;import java.util.List;import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;public class TestArrayQueue3 {// 向队列尾部插入值@Testpublic void offer() {ArrayQueue3<Integer> queue = new ArrayQueue3<>(5);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertIterableEquals(List.of(1, 2, 3, 4, 5), queue);}// 从队列头获取值,不移除@Testpublic void peek(){ArrayQueue3<Integer> queue = new ArrayQueue3<>(5);assertNull(queue.peek());queue.offer(1);assertEquals(1,queue.peek());queue.offer(2);assertEquals(1,queue.peek());queue.offer(3);assertEquals(1,queue.peek());}// 从队头获取值,并移除@Testpublic void poll() {ArrayQueue3<Integer> queue = new ArrayQueue3<>(5);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertEquals(1,queue.poll());assertEquals(2,queue.poll());assertEquals(3,queue.poll());assertEquals(4,queue.poll());assertEquals(5,queue.poll());assertNull(queue.poll());}// 向有限队列头部加入元素@Testpublic void offerLimit(){ArrayQueue3<Integer> queue = new ArrayQueue3<>(3);queue.offer(1);queue.offer(2);queue.offer(3);queue.offer(4);queue.offer(5);assertIterableEquals(List.of(1, 2, 3), queue);}
}

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

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

相关文章

项目实战:构建高效可扩展的Flask Web框架:集成Flask-SQLAlchemy、Marshmallow与日志管理

前言 在Web开发中&#xff0c;构建一个既高效又可扩展的框架是项目成功的基石。Flask作为一个轻量级的Web应用框架&#xff0c;凭借其易用性和灵活性&#xff0c;特别适合快速开发和原型设计。结合Flask-SQLAlchemy&#xff08;为Flask提供SQLAlchemy ORM支持的扩展&#xff0…

剑指 offer 刷题集

目录 数组 1. LCR 121. 寻找目标值 - 二维数组 2. LCR 120. 寻找文件副本 3. LCR 128. 库存管理 I 4. LCR 131. 砍竹子 I 5. LCR 132. 砍竹子 II 6. LCR 135. 报数 7. LCR 139. 训练计划 I 8. LCR 158. 库存管理 II 9. LCR 159. 库存管理 III 10. LCR 160. 数据流中…

计算机毕业设计 在线项目管理与任务分配系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

一文带你读懂分库分表,分片,Sharding的许多概念

一文带你读懂分库分表,分片,Sharding的许多概念 分库是将一个库拆分为多个库&#xff0c;分表就是将一个表拆分为多个表。分库分表有垂直拆分和水平拆分。垂直拆分一般是按照业务将表分到不同的库中&#xff08;此种不在本发的讨论范围&#xff09;。水平拆分是将表的数据拆分…

HEITRONICS TC13红外辐射高温计CT13 INFRARED RADIATION PYROMETER CT13

HEITRONICS TC13红外辐射高温计CT13 INFRARED RADIATION PYROMETER CT13

华为玄玑感知系统震撼发布:智能穿戴新品引领情绪健康新纪元

在科技日新月异的今天&#xff0c;华为再次以其卓越的创新能力&#xff0c;为智能穿戴领域带来了一场革命性的变革。 8月28日&#xff0c;华为玄玑感知系统暨穿戴创新技术发布会圆满落幕&#xff0c;会上正式揭晓了这款名为“玄玑”的神秘感知系统&#xff0c;预示着穿戴设备将…

.netcore nacos注册成功,服务列表找不到任何服务

命令空间id不要自动生成 .netcore 配置文件里&#xff0c;Namespace 配置命名空间id 而不是命名空间名称。

宠物空气净化器希喂和352哪个好用?两大爆火机型哪款吸毛、除臭效果比较好?

猫毛、狗毛、鹦鹉毛&#xff0c;总之只要家里养着有带毛的宠物&#xff0c;毛就会出现在各种地方&#xff0c;床上、沙发上、衣服上、水杯里...根本躲不开。而且&#xff0c;除了肉眼可见的&#xff0c;呼吸时、说话时&#xff0c;不经意间还会吃到毛毛。这些毛毛飘在空气里时&…

动手学LLM(ch2)

2.1 理解词嵌入 深度神经网络模型&#xff0c;包括大型语言模型&#xff08;LLMs&#xff09;&#xff0c;无法直接处理原始文本&#xff0c;因为文本是分类数据&#xff0c;与神经网络的数学运算不兼容。为了达到这个目的&#xff0c;需要将单词转换为连续值向量。记住一句话…

17【Protues单片机仿真】基于51单片机的太阳能智能谷物翻晒机器人

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;避障&#xff0c;低于50CM报警&#xff0c;LED灯亮起&#xff0c;自动翻晒用光敏电阻&#xff0c;光照强度大&#xff0c;电机转动&#xff0c;相当于翻晒粮食&#xff0…

Linux——pod的调度

pod的调度 控制器: rc/rs 副本数量控制器 主要保证pod的数量符合管理员要求&#xff0c;并不会对pod进行额外的管理 以下三种控制器&#xff0c;本质上是服务控制器。具备以下特性&#xff1a; 副本数量的控制服务的滚动更新&#xff08;更新pod&#xff09;支持更新失…

高校教师成果管理小程序的设计与实现springboot(lw+演示+源码+运行)

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对高校教师成果信息管理混乱&#xff0c;出错率高&#xff0c;信息安全…

C++ STL初阶(14): map和set

1.关联式容器与键值对 前导文章&#xff1a;C 二叉树进阶-CSDN博客 之前我们学习的线性的容器&#xff0c;如&#xff1a;vector deque list等都叫作序列式容器 与之对立的概念是关联式容器 关联式容器 也是用来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;其 里面…

【C++】检测TCP链接超时——时间轮组件设计

目录 引言 时间轮思想 设计的核心思路 完整代码 组件接口 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 引言 对于高并发的服务器来说&#xff0c;链接是一种比较珍贵的资源&#xff0c;对不活跃的链接应该及时释放。判断连接是否活跃的策略是——在给定的时间内&#…

Redis中BigKey与MoreKey优化笔记

1.MoreKey 在Redis中&#xff0c;MoreKey问题通常指的是当数据库中的key数量非常多时&#xff0c;使用如KEYS *这样的命令去检索所有的key&#xff0c;这会导致Redis服务阻塞&#xff0c;影响正常业务。因为Redis是单线程操作的&#xff0c;执行这类命令时会占用大量时间&…

Arthas redefine(加载外部的.class文件,redefine到JVM里 )

文章目录 二、命令列表2.2 class/classloader相关命令2.2.3 redefine&#xff08;加载外部的.class文件&#xff0c;redefine到JVM里 &#xff09;举例1&#xff1a;加载新的代码&#xff0c;jad/mc 命令使用举例2&#xff1a;上传 .class 文件到服务器的技巧 二、命令列表 2.…

柯桥韩语学校|韩语每日一词打卡:회갑연[회가변]【名词】花甲宴

今日一词:회갑연 韩语每日一词打卡&#xff1a;회갑연[회가변]【名词】花甲宴 原文:인구 노령화에 따라서 요즘 회갑연보다는 고희연을 더 많이 지냅니다. 意思&#xff1a;随着人口老龄化&#xff0c;最近比起花甲宴&#xff0c;更多人办古稀宴。 【原文分解】 1、인구[인구]…

【BurpSuite】访问控制漏洞和权限提升 | Access control vulnerabilities (3-6)

&#x1f3d8;️个人主页&#xff1a; 点燃银河尽头的篝火(●’◡’●) 如果文章有帮到你的话记得点赞&#x1f44d;收藏&#x1f497;支持一下哦 【BurpSuite】访问控制漏洞和权限提升 | Access control vulnerabilities (3-6&#xff09; 实验三 Lab: User role controlled b…

【IoT-NTN】系统消息SIB31信令分析

3GPP卫星通信发展迅速&#xff0c; TS36.331 R17中新增SIB31携带星历信息&#xff0c;本文对SIB31的信令内容进行了分析。 SystemInformationBlockType31 分析报告 一、概述 本文档详细描述了SystemInformationBlockType31&#xff08;简称SIB31&#xff09;的结构和内容&am…

[Redis][集群][上]详细讲解

目录 0.前言1.基本概念2.数据分片算法0.前言1.哈希求余2.一致性哈希算法3.哈希槽分区算法(Redis使用) 0.前言 说明&#xff1a;该章节相关操作不需要记忆&#xff0c;理解流程和原理即可&#xff0c;用的时候能自主查到即可 1.基本概念 哨兵模式提高了系统的可用性&#xff0…