单调栈和单调队列及其相关应用

news2024/11/19 13:26:27

        前言:好久没更新了,痛苦的期末考试周终于过去了,我可以回来继续更新了,今天我们就来学习单调栈和单调队列的相关知识及其应用,单调栈和单调队列是在算法中常用的两种数据结构,用于解决一些与区间最值相关的问题。它们的主要区别在于操作的顺序不同,单调栈是一种后进先出的数据结构,而单调队列是一种先进先出的数据结构。

目录

1.单调栈

1.1单调栈的例题引入

1.2 单调栈-下标作为栈元素

1.3总体区间最值问题 

1.4部分区间最值问题

2.单调队列 

2.1 单调队列的例题引入

2.2区间最值问题

3.金句频道


1.单调栈

       单调栈是一个栈,它在任何时候都保持单调递增或单调递减。通俗的说,就是从栈底到栈顶的元素可以形成一个单调不降或单调不增的序列。

使用单调栈的主要场景是求解区间最值问题,例如:

  • 求解数组中所有元素的下一个或上一个更大元素,下一个或上一个更小元素。
  • 求解某一区间内最大值或最小值。

使用单调栈可以在O(n)时间内解决上述问题,而使用普通的算法可能需要O(n^2)的时间复杂度。

1.1单调栈的例题引入

       首先,暴力解法是行不通的,因为我们有10^5个数据,每次都从i-1 -> 0进行遍历查找的话,时间就肯定会超时,所以暴力解法不可行。

那么我们现在来看有没有更加省时间的方式:

       首先,我们可以确定的是,类似于暴力解法,我们寻找一个元素的左边第一个比它小的元素,假设当前我们要寻找下标为i处的数的左边第一个比它小的数,那么我们就需要在区间[0,i-1]内遍历进行答案的寻找,这个过程是比较麻烦的,我们能不能采取一种方式,能够将上一次的寻找结果中的无用的值去掉以减少搜索呢?答案肯定是可行的。下面我们来简单推导一下:

        我们现在所担心的就是,前面的保存的解会不会导致后面的检索出现错误的情况,也就是说,前面的解能否正确的给出后面的寻找答案;

        1.单调栈是单调递减或者点掉递增的栈结构,每次入栈需要和栈内的元素作对比,将栈内的元素大于(单调递减栈)或者小于(单调递增栈)将要入栈的元素的元素给删掉以保持其单调特性不被破坏,这也是单调栈的核心思路;

          2.单调栈的特性,让我们可以保存上一次寻找的结果的同时,将本次寻找的下标所代表的元素入栈,可以在下次寻找时,再次按照需要恢复单调栈的方式更新栈,栈顶元素或者栈为空就是当前元素的寻找结果,并且如此做,保证了,每次的最小值都会保存在栈顶中,也能够确保结果的正确性。

所以,上述的例题,采用单调栈的方式可以如下图:

 有了上面的知识,我们可以来尝试构建代码了:

#include<bits/stdc++.h>
#include <set>
using namespace std;
const int maxm=100010;
int n;
int a[maxm];
stack<int> st;
int res[maxm];
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
      scanf("%d",&a[i]);
    for(int i=0;i<n;i++)
    {
        //此时待入栈的元素是a[i],我们需要将加入a[i]后的栈再次变为单调栈,在该题的背景下,我们需要构造单调递减栈,使得栈顶元素是最小的
        while(!st.empty()&&st.top()>=a[i])//将大于a[i]的栈内的元素删掉
             st.pop();
        if(!st.empty())//如果栈内还有元素,那么一定是a[i]的左侧第一个比a[i]小的数了
           res[i]=st.top();
        else
           res[i]=-1;//如果栈空了,说明没有找到
        st.push(a[i]);//最后,我们需要将a[i]入栈,以保证后续的更新栈和寻找结果正确性
    }
    for(int i=0;i<n;i++)
       printf("%d ",res[i]);
    
    return 0;
}

1.2 单调栈-下标作为栈元素

       有时候,我们需要的不是返回某个数的左侧或者右侧的第一个小或者大的数本身,而是返回该数所在的下标,那么我们就可以将对应的数的下标进行入栈,本质上是一样的,就是换了个考察方式而已。

输入样例:

6 
3 
2 
6 
1 
1 
2 

输出样例:

3 
3 
0 
6 
6 
0 

对于如何确定单调递减栈还是单调递增栈:

       区间最值问题

解决区间最值问题时,需要使用单调栈。对于求解具体的区间最值问题,可以考虑如下方法:

  • 对于求解区间最大值问题,可以建立单调递减栈。如此,栈中的元素都是单调递减的,满足任意元素与当前元素组成区间时,当前元素为区间最大值。
  • 对于求解区间最小值问题,可以建立单调递增栈。如此,栈中的元素都是单调递增的,满足任意元素与当前元素组成区间时,当前元素为区间最小值。

      贪心算法问题

      贪心算法中,需要对数据进行逐步筛选或选择,以达到期望的最优化。在这类问题中,通常需要保证当前处理的数据与之前处理的数据保持单调性。如果当前数据与之前处理的数据单调递增,可以建立单调递增栈。反之,如果单调递减,可以建立单调递减栈。一般来说,需要的元素是比某个元素更大的,就让努力让栈顶的元素保持最大,反之亦然。

       从上述我们可以知道,右侧的更高的牛和左侧的矮一些的牛呈单调递增,也就是说,我们需要的是一个元素右侧对应的下一个更高的,所以我们需要保证更高的栈顶,所以说我们需要建立单调递增栈,第二,处理编号问题,我们可以用单调栈存储元素的值改为存储元素的下标即可。

#include<bits/stdc++.h>
#include <stack>
using namespace std;

const int maxm=1e6+5;
int h[maxm];
int n;
stack<int>m;
int res[maxm];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
      scanf("%d",&h[i]);
    for(int i=n;i>=1;i--)
    {
        while(!m.empty()&&h[m.top()]<=h[i])//现在的栈中储存的是数对应的下标,当前元素比栈顶元素大时,我们需要将小的元素出栈,来找到大的元素
             m.pop();
        if(!m.empty())
           res[i]=m.top();//最终结果是元素对应的下标
        else
           res[i]=0;
        m.push(i);//这次我们需要将元素对应的编号入栈
    }
    for(int i=1;i<=n;i++)
      printf("%d\n",res[i]);
    return 0;
}

1.3总体区间最值问题 

输入样例:

12
0 1 0 2 1 0 1 3 2 1 2 1

输出样例:

6

 思路分析:

1.单调递减栈的建立

          由题意,我们知道需要求解积水量,而我们每读入一个元素的高度,需要向左找到第一个大于该元素的下标位置处,两者之间才能形成积水区域,所以我们需要建立的是一个单调递减栈,每次可以通过新的元素入栈而找出与该高度左侧的可能形成的“坑”的面积,从而求出积水量。

2.积水面积的计算

          该题我们还是利用单调栈,在每一个高度入栈时,用单调栈找每个障碍物左边第一个比它高的位置,累加两障碍物之间的储雨量,注意最后栈内元素是呈降序排列的。字不如图,我们来画图分析:

#include<bits/stdc++.h>
#include<stack>
using namespace std;
const int maxm=100005;

int n;//个数
int h[maxm];
stack<int> idx;//这个栈用来保存下标
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
      scanf("%d",&h[i]);
    int ans=0;//保存最后的结果
    for(int i=1;i<=n;i++)
    {
        //我们首先需要找到一个满足能形成“坑”的位置,也就是某个位置的前面存在比它低的位置处,这里我们的单调栈是单调递减的,我们按栈内的元素向前查找,如果找到了比当前元素矮的
        int bottom=0;//我们按层次来计算每一层的积水量
        while(!idx.empty()&&h[idx.top()]<h[i])
        {
            ans+=(h[idx.top()]-bottom)*(i-idx.top()-1);//积水量等于该层的高度*该层的宽度
            bottom=h[idx.top()];//更新最底部
            idx.pop();
        }
        if(!idx.empty())//如果最后栈还是不为空了,说明当前元素的高度还是能存储积水(也就是说当前的h[i]不是最高的
           ans+=(h[i]-bottom)*(i-idx.top()-1);
        idx.push(i);//新插入的下标入栈
    }
    printf("%d\n",ans);
    
    
    return 0;
}

1.4部分区间最值问题

输入样例:

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

输出样例:

8
4000

 解题思路:

1.如何判断由某一个基准矩形快所构成的最大矩形的面积

       我们需要首先来分析对于其中的一小块矩形块,我们如何求解由该矩形块和其他的矩形块所构成的最大的连通的矩形的面积,这里可能会存在两种情况:

        可见,对于每一个基准块来说,小于其高度所形成的最大矩形面积可以由其他的较矮的矩形块求出,而上述的红色区域的面积却因为受基准块高度的限制而只能求解一次,所以,本着宁可错选而不漏选的原则,我们在由一个基准块求解其所能构成的最大矩形的面积的时候,我们需要将该矩形块的高度看做限制条件,只要左右两侧的矩形块的高的比它矮,我们就找到了该矩形块的边界,这样我们边可以和单调递增栈结合起来,相当于找到一个元素对应的左侧和右侧的第一个比该元素小的值,也就找到了左右边界,矩形面积也就可以求出来了。

2.数组模拟单调栈以及哨兵位的设置省去判断栈为空和边界情况

     前面我们的单调栈的例题中,需要对每一的高度判断栈是否为空,在本题中,我们可以采用数组模拟栈,我们可以加入哨兵位来让栈中永远都有元素,而这个元素需要根据题意来设计,这里我们在不影响答案求解的情况下,可以将哨兵位设在0和n+1的下标处(高度存储是从下标1开始的),因为我们需要的是一个能够找到左右两侧第一个比待选元素小的元素,所以我们需要一个单调递增栈,所以我们设h[0]=h[n+1]=-1(只要比题目中的任何高度都小就行)保证栈底始终存在元素-1即可。然后我们就可以按照求解左右侧第一个小于元素的模式来解题。

代码实现:

#include <bits/stdc++.h>
using namespace std;

const int maxm=100010;
int n;
int h[maxm],l[maxm],r[maxm];//其中l和r数组分别代表在下标位置为i处的左边界和右边界
int q[maxm];//数组模拟栈

int main()
{
    while(scanf("%d",&n),n)//逗号表达式,取最后一项的值
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&h[i]);
        h[0]=h[n+1]=-1;//左右设置两个虚拟的哨兵,可以不用判断越界或者栈为空的情况
        int tt=0;
        q[tt]=0;
        for(int i=1;i<=n;i++)
        {
            while(h[q[tt]]>=h[i]) tt--;//向左侧找到以h[i]为高度的左边界(也就是第一个小于h[i]的位置处
            l[i]=q[tt];
            q[++tt]=i;//将新的下标入栈
        }
        
        tt=0;//清空栈
        q[tt]=n+1;//末尾的虚拟下标入栈(为了防止栈为空)
        for(int i=n;i>=1;i--)
        {
            while(h[q[tt]]>=h[i]) tt--;
            r[i]=q[tt];
            q[++tt]=i;
        }
        long long  res=0;//可能会爆int,数据范围开大些
        for(int i=1;i<=n;i++)
        {
            res=max(res,(long long)h[i]*(r[i]-l[i]-1));
        }
        printf("%lld\n",res);
    }
    return 0;
}

2.单调队列 

单调队列是一种队列,它同样保持单调递增或单调递减。使用单调队列的场景包括:

  • 在一个滑动窗口中求解最值问题。
  • 求解图中的最短路径问题。

单调队列可以在O(n)时间内解决上述问题,而使用普通的算法可能需要O(n^2)的时间复杂度。

2.1 单调队列的例题引入

 

输入样例:

8 3
1 3 -1 -3 5 3 6 7

输出样例:

-1 -3 -3 -3 3 3
3 3 5 5 6 7

我们还是从暴力解法中寻找可以优化的点,我们现在单独拿最大值来看:

由于我们需要求出的是滑动窗口的最大值。

       如果当前的滑动窗口中有两个下标 i 和 j ,其中i在j的左侧(i<j),并且i对应的元素不大于j对应的元素(nums[i]≤nums[j]),则:

       当滑动窗口向右移动时,只要 i 还在窗口中,那么 j 一定也还在窗口中。这是由于 i 在 j 的左侧所保证的。

        因此,由于 nums[j] 的存在,nums[i] 一定不会是滑动窗口中的最大值了,我们可以将nums[i]永久地移除。

        因此我们可以使用一个队列存储所有还没有被移除的下标。在队列中,这些下标按照从小到大的顺序被存储,并且它们在数组nums中对应的值是严格单调递减的。

        当滑动窗口向右移动时,我们需要把一个新的元素放入队列中。为了保持队列的性质,我们会不断地将新的元素与队尾的元素相比较,如果新元素大于等于队尾元素,那么队尾的元素就可以被永久地移除,我们将其弹出队列。我们需要不断地进行此项操作,直到队列为空或者新的元素小于队尾的元素。

        由于队列中下标对应的元素是严格单调递减的,因此此时队头下标对应的元素就是滑动窗口中的最大值。

代码实现:

#include<bits/stdc++.h>

using namespace std;

const int maxm=1000010;
int n,k;
int a[maxm],q[maxm];//数组和q模拟单调队列,注意q中存储的是下标

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
      scanf("%d",&a[i]);
    int st=0,ed=-1;//队头和队尾置空
    for(int i=0;i<n;i++)
    {
        if(i-k+1>q[st])//大于说明保存长度为k的队列就要向右移动,且一次移动一个位置
           ++st;
        while(st<=ed&&a[q[ed]]>=a[i])//目的是找到当前队列中的最小的值将其置为队尾 
           --ed;
        q[++ed]=i;//新的下标入队
        if(i+1 >= k)//说明满k个可以输出了
           printf("%d ",a[q[st]]);
        
    }
    printf("\n");
    st=0,ed=-1;
    for(int i=0;i<n;i++)
    {
        if(i-k+1>q[st]) ++st;
        while(st<=ed&&a[q[ed]]<=a[i]) --ed;
        q[++ed]=i;
        if(i+1 >= k)
          printf("%d ",a[q[st]]);
    }
   
    return 0;
}

2.2区间最值问题

思路分析:

前缀和:

       前缀和的基本思想是,预处理一个数组 Sum,其中 Sum[i] 表示 num[0] + num[1] + … + num[i-1](也就是前 i 个元素的和),那么 num[i] 到 num[j] 的区间和就可以通过计Sum[j+1] - Sum[i] 得出。这样以来,我们可以在 O(n) 的时间复杂度内计算出整个数组的前缀和,并且前缀和数组可用于 O(1) 的时间复杂度内计算任意区间的和。实际中为了方便,我们一般都是将数组从下标1开始存数,这样前缀和的下标和数组的下标就能更好的对应起来。

那么这道题目如何处理呢?

        我们发现我们的目标就是找到两个位置l, r 满足要求 r−l≤m 也就是说区间的大小不可以超过m
sum[r]−sum[l]尽量大.
        这么说来,我们完全可以O(n^2)枚举l, r不就好了吗?但是时间上肯定是做不到的,所以我们尝试着用单调队列来进行优化:

 代码如下:

#include <bits/stdc++.h>
using namespace std;

const int maxm=300010;
int n,m;
int s[maxm],q[maxm];//前缀和数组s和q数组模拟队列

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&s[i]);
        s[i]+=s[i-1];//直接求前缀和数组,原数组对我们是无用的,所以我们可以直接对其修改为前缀和数组s
    }
    
    int st=0,ed=0;//队头队尾赋初值
    int res=INT_MIN;//保存最大子段和
    for(int i=1;i<=n;i++)
    {
        if(i-q[st]>m) st++;//队头出队,注意这里其实是让队列中保持m+1个元素,因为我们的队头元素必须是sum[j-1],[i,j]前缀和,计算必须用到sum[j-1],而不是sum[j]
        //去除逆序的元素并计算最优值
        res=max(res,s[i]-s[q[st]]);
        //去除逆序的元素,使队列再次单调
        while(st<=ed&&s[q[ed]]>=s[i]) --ed;
        //加入当前元素
        q[++ed]=i;
    }
    printf("%d\n",res);
    return 0;
}

       

         关于单调队列的联系,个人还有待进一步加强,后续也会继续更新相关的内容,文章内容较多,所以这里就不再详述了。

3.金句频道

        千万别用年轻时的懒惰和放纵,换来一生的后悔,因为,我们现在的努力里,藏着我们十年后的样子,凡事想着蒙混过关,“摸鱼划水”,困难只会越来越多。

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux5.9 MySQLMHA高可用配置及故障切换

文章目录 计算机系统5G云计算第四章 LINUX MySQL MHA高可用配置及故障切换一、概述及原理1.什么是 MHA2.MHA 的组成&#xff08;工作原理&#xff09;3.MHA 的特点 二、MMM&#xff08;Master-Master replication manager for MvSQL&#xff0c;MySQL主主复制管理器&#xff09…

Promise 深度学习

文章目录 Promise 由来Promise的用法reject的用法finally all的用法race的用法总结 Promise 由来 我们处理异步函数最普通的方法是这样的&#xff0c;等待上一次请求结束再执行下一步操作&#xff1a; // 一般以定时器来模拟一次请求 setTimeout(() > {console.log("…

爬虫如何通过HTML和CSS采集数据的 ?

爬虫可以应用于各种应用场景&#xff0c;包括数据分析、市场研究、舆情监测、竞争报、价格比较、内容聚合等。对于需要大量数据的业务和研究领域&#xff0c;爬虫能够提供宝贵的支持。 爬虫可以按照设定的规则从多个网进行批量数据抓取&#xff0c;比人工手动方式更高效。量数据…

什么是DevOps

什么是DevOps 1.概述附录 1.概述 >什么是DevOps与CICD 附录 1.什么是DevOps

【Leetcode】19.删除链表的倒数第 N 个结点

一、题目 1、题目描述 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 示例1: 输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]示例2: 输入:head = [1], n = 1 输出:[]示例3: 输入:head = [1,2], n = 1 输出:[1]提示: 链表中结点的数目为 sz…

通付盾升级数信云4.0,利用人工智能、区块链及Web3安全技术助力数据要素市场化

数据作为新型生产要素已成为数字时代的核心生产力。人工智能、区块链、云计算等新兴技术提升了数据要素的使用效率&#xff0c;开启数字化浪潮。但同时&#xff0c;以“数据上云”为代表的数据应用趋势也带来了数据安全、数据隐私和数据共享难等一系列阻碍数据要素化的问题。20…

excel相关操作

文章目录 1、数据分列与绘图1.1、杂乱的数据拷贝到excel1.2、 智能分列1.2 或者手动设置分列1.3、杂论的符号替换掉1.4、对时间再次只能分裂1.5、绘图 1、数据分列与绘图 1.1、杂乱的数据拷贝到excel 1.2、 智能分列 选择数据&#xff0c;数据–>分列–> 智能分列 结…

多服务器云探针源码(服务器云监控)

全球服务器分布世界地图 服务器&#xff08;控制端&#xff09; ping 连通率功能 后台编辑 添加 删除 服务器&#xff08;控制端&#xff09; 生成服务器&#xff08;控制端&#xff09;一键安装脚本 在线 SSH 多服务器批量执行命令 多服务器定制分享地址 控制游客那些服…

汽车行业app开发,汽车保养APP开发需要多少钱?

在现代社会&#xff0c;随着科技的快速发展&#xff0c;手机APP已经成为了我们生活中不可或缺的一部分&#xff0c;从旅游预订、餐饮外卖到汽车保养&#xff0c;各种行业都在尝试通过APP来提供更方便、更个性化的服务。对于汽车行业而言&#xff0c;一款优质的汽车保养APP不仅能…

计算机组成原理(期末或考研备考)-计算机性能指标(字长,主存容量,吞吐量,主频和时钟周期)

字长&#xff1a;字长是指计算机进行一次整数运算所能处理的二进制数据的位数&#xff0c;通常与CPU寄存器大小相同&#xff0c;因为数据进入到CPU之前会放入寄存器中。 主存大小&#xff1a;通常使用字数字长&#xff0c;例如512K*16位就表示共有512K个存储单元&#xff0c;每…

回归预测 | MATLAB实现基于QPSO-BiGRU、PSO-BiGRU、BiGRU多变量回归预测

回归预测 | MATLAB实现基于QPSO-BiGRU、PSO-BiGRU、BiGRU多变量回归预测 目录 回归预测 | MATLAB实现基于QPSO-BiGRU、PSO-BiGRU、BiGRU多变量回归预测效果一览基本描述程序设计参考资料 效果一览 基本描述 1.Matlab实现QPSO-BiGRU、PSO-BiGRU和BiGRU双向门控循环单元多变量回归…

Win11转移conda环境——从C盘转移到D盘

原本就是将Anaconda安装到了C盘&#xff0c;结果发现环境他还是默认安装到了C盘&#xff0c;就打算再转移一下 conda版本为 首先找到Win11下面的环境位置 C:\Users\用户名\.conda\envs比如我的就是 C:\Users\Arona\.conda\envs找到D盘安装Anaconda的位置 D:\ProgramData\an…

vue项目系统内路由无权限跳转404改为跳转401

当客户进入新页面无权限时,打开一个新页签,提示客户“sorry! 你没有权限去该页面” 。系统外路由还是跳转404. 解决思路: 这里需要后端提供个接口,传当前路由,如果是系统内路由接口返回true,如果不是返回false. 在路由跳转404的时候进行调接口判断。如果返回true .则跳…

数字化转型|银行业数据中心数字化转型之模型篇 02

导语&#xff1a; 银行业数据中心数字化转型是一项系统性工程&#xff0c;既涉及管理层面转型——包括数字化转型战略、基础架构和技术架构转型、技术创新和知识体系转型&#xff0c;又涉及执行层面转型——包括人员管理&#xff08;P&#xff09;、流程管理&#xff08;P&…

CAD Voronoi 3D三维泰森多边形维诺图插件三维狄利克雷镶嵌(Dirichlet tessellation)

三维Voronoi Voronoi又名泰森多边形或Dirichlet图、维诺图等&#xff0c;三维Voronoi是由连接两邻点直线的垂直平分面组成的连续三维多面体结构。Voronoi在各个学科中应用广泛&#xff0c;如进行区域规划、晶体塑性有限元研究、路径优化、地形简化、多孔结构力学等方面的分析。…

PSD笔记

在实际应用中&#xff0c;一个信号我们不可能获得无穷长时间段内的点&#xff0c;对于数字信号&#xff0c;只能通过采样的方式获得N个离散的点。上文提到&#xff0c;实际信号基本上是随机信号&#xff0c;由于不可能对所有点进行考察&#xff0c;我们也就不可能获得其精确的功…

DETR:End-to-End Object Detection with Transformers笔记

文章目录 End-to-End Object Detection with Transformers摘要本文方法损失函数 代码实现 End-to-End Object Detection with Transformers 摘要 提出了一种将目标检测视为直接集预测问题的新方法。我们的方法简化了检测管道&#xff0c;有效地消除了许多手工设计的组件&…

Win10实时保护老是自动开启怎么办?

Win10实时保护老是自动开启怎么办&#xff1f;使用Win10电脑的用户遇到了实时保护老是自动开启的问题&#xff0c;想知道怎么操作才能解决此问题&#xff0c;这时候用户需要打开电脑的组策略编辑器&#xff0c;然后找到管理模板中的Windows Defender选项&#xff0c;点击关闭Wi…

Intellij IDEA HTTP Request 请求设置Cookie

使用Intellij IDEA 的 HTTP Request 请求中带有Cookie时&#xff0c;需要将 Cookie单词要写成全小写的“cookie”&#xff0c;否则设置的Cookie不会生效。 POST http://localhost:9091/rest/miracle/findList Content-Type: application/json cookie: JSESSIONIDce22a4ed-b185…

Redis基本数据类型

string&#xff08;字符串&#xff09;&#xff1a;最常见的用户是缓存用户信息&#xff0c;将用户信息结构体使用JSON序列化成字符串&#xff0c;然后将序列化后的字符串塞进Redis来缓存&#xff0c;然后取用户信息的过程会经历一次反序列化的过程。 Redis的字符串是动态字符…