【C++】stack | queue | priority_queue | deque

news2024/11/24 0:24:56

一、stack栈

介绍

1.栈是一种特殊的线性表,其元素遵循“后进先出”的原则,即仅允许在在表的一端进行插入、删除操作,这一模式被称为“后进先出”或LIFO(last in fisrt out)。

2.从底层实现来看,stack是作为容器适配器被实现的,什么是容器适配器?我们来解释一下先。

先来看看我们身边的适配器。比方说,你有注意到笔记本电脑的充电器吗?

其实,笔记本的充电器就是一个适配器。适配器要做的就是电压的转换。一般来说,电脑的电池电压为14V左右,而标准电压为220V,要想给电脑充电,这就需要对标准电压进行转换,这就是适配器发挥的作用,适配就是转换的意思。

适配器是一种设计模式,该模式将一个类的接口转换为用户希望的另一个类的接口,你可以认为适配器是一种转换器。

容器适配器,能让程序员选择一种合适的底层数据结构。如stack,之前我们写c语言时,要用到栈的数据结构时,是不是得还先模拟一个栈出来?

那现在就不用这么麻烦了,要用到栈和队列的地方,直接用就行

3.stack的底层容器可以是 任何容器类模板 or 其他特定的容器类。

这些容器类应该支持以下操作:

empty:判空操作

back:获取尾部元素操作

push_back:尾部插入元素操作

pop_back:尾部删除元素操作

栈的底层实现(简易版):

可以看到,栈的这些方法都是直接复用容器的方法,体现了代码复用的思想。

这里的Container就是容器类,它可以是vector、deque、list,也可以省略不传。若省略,那默认情况下是deque类。

使用

使用时要包<stack>头文件。

栈提供的方法有:判空、取大小、取栈顶数据、压栈(插入)、出栈(删除)、交换 还有emplace安放。

这里对其中几点方法做个说明:

1.构造函数:

stack<Type, Container> (<数据类型,容器类型>) stackName;

数据类型一定要有,容器类型可以是vector、deque、list。(容器类型可省略,默认是deque)

2.在用top()前,得判断下栈是否为空。只有不为空的时候,才能调用top(),不然会发生段错误。

这是因为,在栈为空时,stack 的top函数返回的是超尾-1,而不是NULL。

3.和上面的top一样,在调用pop函数前,也要确保栈中至少有一个元素。

如果栈为空,调用pop会抛出EmptyStackException异常。

4.关于emplace,意为“安放”,就是说,构造一个新的元素放入栈顶。emplace和push很像,这里做一下区分。

push的话,得是你先构造好对象,然后push插进栈里;emplace则是主动帮你调用构造函数,构造出对象,然后插进栈里。

示例:

#include<iostream>
#include<stack>
using namespace std;
int main() {
    stack<int> st;
    st.push(1);
    st.push(2);
    st.push(3);
​
    while (!st.empty()) {
        cout << st.top() << " ";
        st.pop();
    }
    cout << endl;
    return 0;
}

从底层来看,Container是一个模板,你传数组or链表都可以。

来看看它是怎么适配的:

二、queue队列

介绍

1.队列是一种特殊的线性表,它只能在队尾插入数据,队头出数据,这一模式被称为“先进先出”或FIFO(first in first out)。

2.从底层实现来看,queue也是作为容器适配器被实现的。

展示下queue简易版的底层实现:

底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。

该底层容器应至少支持以下操作:

empty:检测队列是否为空

size:返回队列中有效元素的个数

front:返回队头元素的引用

back:返回队尾元素的引用

push_back:在队列尾部入队列

pop_front:在队列头部出队列

标准容器类deque和list满足了这些要求。也就是说,Container的位置可以传deque/ list过去,也可以省略不传。当省略不传时,那默认Container是deque

(为什么vector不满足呢?因为vector没有头删。之前我们说过,vector嫌头删效率低就没实现)

使用

使用时要包头文件<queue>。

根据队列的特性,实现的方法有:判空、取大小、取队头、取队尾、(队尾)插入、(队头)删除、交换 和emplace安放。

几点说明:

1.构造函数:

queue<Type, Container> (<数据类型,容器类型>) queueName;

在初始化时必须要有数据类型,容器可省略,省略时默认为deque 类型。

所以构造一个queue可以是这样两种情况:

queue<int> q;
​
queue<int,list<int>> q;

示例:

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
int main() {
    queue<int> q;
    q.push(1);
    q.push(2);
    q.push(3);
​
    while (!q.empty()) {
        cout << q.front()<<" ";
        q.pop();
    }
    cout << endl;
    return 0;
}

这张图说明了,为什么deque是容器适配器:

三、priority_queue优先级队列

介绍

1.优先级队列是一种特殊的队列结构,它队列中的顺序并非插入的顺序,而是按权重来排序。

它给每个元素加上了优先级,每次出队的元素是队列中优先级最高的那个元素。

注:不是越大的元素,优先级就越高。

那优先级是如何评定的呢?其实,优先级队列在创建伊始,它的优先级就已经定好了,默认为降序。

那要如何排升序呢?一会会在仿函数那说到。

2.优先级队列 底层被实现为容器适配器。

底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。

容器应该可以通过随机访问迭代器访问,并支持以下操作:

empty():检测容器是否为空

size():返回容器中有效元素个数

front():返回容器中第一个元素的引用

push_back():在容器尾部插入元素

pop_back():删除容器尾部元素

容器类vector和deque满足这些需求。如果容器类被省略不写,那默认使用vector。

3.需要支持随机访问迭代器,以便始终在内部保持堆结构。

priority_queue是用堆实现的,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue。

注:默认情况下priority_queue是大堆。

使用

使用时要包头文件<queue>。

注:取顶端数据不是用front了!是用top。

示例:

#include<iostream>
#include<queue>
using namespace std;
int main() {
    priority_queue<int> q;
    q.push(4);
    q.push(12);
    q.push(3);
​
    while (!q.empty()) {
        cout << q.top()<<" ";
        q.pop();
    }
    cout << endl;
    return 0;
}

模拟实现

priority_queue具有队列的所有特性,只是在此基础上又在内部加入了排序。它的底层原理是用堆实现的。

现在我们来模拟实现一下它的简易版,这样能更好地理解它的底层原理。

➡️复用代码

先把top、empty这种,通过复用代码而实现的 函数给写了:

#pragma once
#include<iostream>
#include<vector>
using namespace std;
namespace jzy
{
    template<class T,class Container = vector<T>>  //T是优先级队列中的 数据存储类型
    class priority_queue                            //Container是优先级队列中的数据结构
    {
    public:
        void push(const T& val) {}
        void pop() {}
        T top() {
            return _con.front();   //直接复用容器的方法
        }
        bool empty() {
            return _con.empty();
        }
        size_t size() {
            return _con.size();
        }
​
    private:
        T _val;
        Container _con;
    };
}

➡️push

push的实现:(大根堆)先尾插再向上调整。

void AdjustUp(Container& _con) {
    int child = _con.size()-1;
    int parent = (child - 1) / 2;
​
    while (child > 0){
        if (_con[child] > _con[parent]) {
            swap(_con[child], _con[parent]);
            child = parent;
            parent = (child - 1) / 2;
        }
        else {
            break;
        }
    }
}
​
void push(const T& val) {
    _con.push_back(val);
    AdjustUp(_con);
}

➡️pop

pop的实现:先把首尾交换,让优先级最高的元素来到队尾,以便尾删。再向下调整。

void AdjustDown(Container& _con) {
    int parent = 0;
    int child = 2 * parent + 1;
​
    while (child < _con.size()) {
        if (child + 1 < _con.size() && _con[child + 1] > _con[child]) {
            child++;
        }
​
        if(_con[parent] <_con[child]) {
            swap(_con[parent], _con[child]);
            parent = child;
            child = 2 * parent + 1;
        }
        else {
            break;
        }
    }
}
​
void pop() {
    swap(_con.front(), _con[_con.size()-1]);
    _con.pop_back();
    AdjustDown(_con);
}

➡️如何排降序

目前排的是升序,那要想排降序,要怎么办呢?

我倒是有个很朴素的办法:每次插入元素,向上调整时,我把小的往上调,不就行了吗?

其实就只要改个符号:

void AdjustUp(Container& _con) {
    int child = _con.size()-1;
    int parent = (child - 1) / 2;
​
    while (child > 0){
        if (_con[child] < _con[parent]) {    //这里原本是>,现在给改成<。越小越往上调
            swap(_con[child], _con[parent]);
            child = parent;
            parent = (child - 1) / 2;
        }
        else {
            break;
        }
    }
}

但是,这种方法并不好。我想排升序,要把符号改成>;想排降序,又要改回<,麻烦。

能不能用泛型解决呢?

其实在STL库里,就是用泛型解决的,我们来学习一下。

STL里,priority_queue有3个模板参数:

template <class T, class Container = vector<T>, class Compare = less<typename Container::value_type>>

第三个模板参数Compare,定义了比较的方式。就是说,我们通过定义Compare类,来制定比较的规则。

不过,要想理解参数Compare,我们得先学习一个知识点:仿函数。

仿函数

仿函数(functor)不是函数,它描述的是对象,只不过这个对象重载了operator(),所以它使用起来像函数一样。

比方说,你定义了一个A类,用A实例化出对象a。此时a是一个普通的对象。

但当你在A里面重载了operator()(即函数调用运算符),那a就是仿函数,就可以这样用:

class A
{
  ……
  int operator() (int x,int y){
    return x+y;
  }
};
int mian(){
    A a;
    cout<<a(1,2);   //A实例化出的对象a,就可以像函数一样使用,a就叫仿函数
    return 0;
}

关于():

我一开始大为不解,怎么圆括号()也能重载?

没错!()叫做”函数调用运算符“,我们在调用函数时,往往要传参,此时就用到这个运算符。

所以说,要想让对象成为仿函数,只需要在它的类里面重载operator()。

用仿函数实现升降序

先实现两个类:Greater、Less,分别用于定义升序、降序(这俩类实现为模板)。

然后在priority_queue类中增加一个模板参数compare,此参数用于接收 比较方式 的类型(是升序,还是降序)。

compare默认为降序。当我们想要升序,那就在传参时,实例化出Greator类型的对象,传过去即可。

//先实现Less和Greater两个类,来定义比大小的规则
template<class T>
struct Less    //less意为越来越小,即降序
{
    bool operator() (const T& x, const T& y) {
        return x > y;
    }
};
​
template<class T>
struct Greater     //升序
{
    bool operator() (const T& x, const T& y) {
        return x < y;
    }
};
​
template<class T,class Container = vector<T>,class Compare=Less<T>>  //增加模板参数,默认为降序
class priority_queue
{
    Compare com;
public:
    void AdjustUp(Container& _con) {
        int child = _con.size()-1;
        int parent = (child - 1) / 2;
​
        while (child > 0){
            if (com(_con[child],_con[parent])) {
                swap(_con[child], _con[parent]);
                child = parent;
                parent = (child - 1) / 2;
            }
            else {
                break;
            }
        }
    }
    void AdjustDown(Container& _con) {
        Compare com;
        int parent = 0;
        int child = 2 * parent + 1;
​
        while (child < _con.size()) {
            if (child + 1 < _con.size() && com(_con[child+1],_con[child])) {   
                child++;
            }
​
            if(com(_con[child], _con[parent])) {
                swap(_con[parent], _con[child]);
                parent = child;
                child = 2 * parent + 1;
            }
            else {
                break;
            }
         }
    }
    void push(const T& val) {
        _con.push_back(val);
        AdjustUp(_con);
    }
    void pop() {
        swap(_con.front(), _con[_con.size()-1]);
        _con.pop_back();
        AdjustDown(_con);
    }
    ……
};

注:因为我们展开了STL库,所以在命名Less/Greater时,注意不要写成less/greater,不然就和库里的重名了,编译器就就晕了 不知道用哪个。

测试:排升序

#include"priority_queue.h"
using namespace jzy;
int main() {
    priority_queue<int,vector<int>,Greater<int>> pq;
    pq.push(7);
    pq.push(3);
    pq.push(8);
    pq.push(2);
    pq.push(12);
    pq.push(6);
    pq.push(3);
​
    while (!pq.empty()) {
        cout << pq.top() << " ";
        pq.pop();
    }
    return 0;
}

模拟实现的总代码

priority_queue.h:

#pragma once
#include<iostream>
#include<vector>
using namespace std;
namespace jzy
{
    template<class T>
    struct Less    //less意为越来越小,即降序
    {
        bool operator() (const T& x, const T& y) {
            return x > y;
        }
    };
​
    template<class T>
    struct Greater     //升序
    {
        bool operator() (const T& x, const T& y) {
            return x < y;
        }
    };
​
    template<class T,class Container = vector<T>,class Compare=Less<T>>
    class priority_queue
    {
        Compare com;
    public:
        void AdjustUp(Container& _con) {
            int child = _con.size()-1;
            int parent = (child - 1) / 2;
​
            while (child > 0){
                if (com(_con[child],_con[parent])) {
                    swap(_con[child], _con[parent]);
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else {
                    break;
                }
            }
        }
        void AdjustDown(Container& _con) {
            Compare com;
            int parent = 0;
            int child = 2 * parent + 1;
​
            while (child < _con.size()) {
                if (child + 1 < _con.size() && com(_con[child+1],_con[child])) {   
                    child++;
                }
​
                if(com(_con[child], _con[parent])) {
                    swap(_con[parent], _con[child]);
                    parent = child;
                    child = 2 * parent + 1;
                }
                else {
                    break;
                }
            }
        }
        void push(const T& val) {
            _con.push_back(val);
            AdjustUp(_con);
        }
        void pop() {
            swap(_con.front(), _con[_con.size()-1]);
            _con.pop_back();
            AdjustDown(_con);
        }
        T top() {
            return _con.front();
        }
        bool empty() {
            return _con.empty();
        }
        size_t size() {
            return _con.size();
        }
​
    private:
        T _val;
        Container _con;
    };
}

四、deque双端队列

(这个容器了解即可,这个容器很少用)

介绍

1.deque是“double-ended queue”的缩写。

虽然叫双端队列,但它和队列没啥关系。deque是一种双开口的"连续"空间的数据结构。双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1)。

deque支持随机访问,也支持任意位置的插入、删除。似乎,deque结合了vector和list的优点。

但,deque真的是完美的吗?

并不是!deque随机访问的效率比较低。至于为什么,那先让我们了解一下它的底层实现。

底层实现

和vector不同,deque的底层并不是 真正意义上的连续的空间,而是由一段段连续的小空间拼接而成的,这些小空间不一定是连续的,可能是位于内存的不同区域。如图:

这里的map,并不是STL里的map容器。而是数组,数组的每个元素都是 指向另一块连续空间(缓冲区buffer)的 指针。我们插入的元素,实际上是存进buffer里的。

可见,deque对空间利用率很高,几乎没什么空间的浪费。

➡️如何进行扩容呢?

当一个buffer存满了,要尾插,此时不会给当前buffer扩容(因为它是大小固定的),而是再开一个buffer空间,尾插的内容存进新的buffer里。指针数组的下一个元素指向新的buffer。头插同理。下图可以帮助我们理解:

可见,头插、尾插不需要挪动数据,效率自然高。

如果map满了,那就再开一个更大的map,如何把数据拷到新的map里。

➡️关于deque的迭代器,它的原理很复杂,由四个指针组成。node指向中控区的指针数组,first和node分别指向一段空间的开始和结束,cur指向访问到的位置。如果访问的元素不在当前空间,cur就等于下一个空间的first,再继续访问。

➡️当需要随机访问时,deque得先完整复制到一个vector上,如何vector排序后,再复制deque。这就导致了deque的随机访问效率较vector更低。

关于deque的小结

优势:1.相比vector,deque的头插、头删不需要挪动数据,而是直接开新buffer插入,效率很高;

在扩容时,也无需开新空间、拷数据,而是直接开个新buffer,效率高。

2.相比list,deque的底层是连续的存指针的map数组,空间利用率比较高,不需要存储额外字段。况且,deque还支持随机访问,虽然效率不高。

劣势:1.不适合遍历。

因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下。

而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。

2.中间插入、删除的效率并不高。(因为下标需要经过计算,并且要挪动元素)

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

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

相关文章

07-MySQL-进阶-锁InnoDB引擎MySQL管理

涉及资料 链接&#xff1a;https://pan.baidu.com/s/1M1oXN_pH3RGADx90ZFbfLQ?pwdCoke 提取码&#xff1a;Coke 一、锁 ①&#xff1a;概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中&#xff0c;除传统的计算资源&#xff08;CPU、RAM、I/O&#xf…

【AI编程】ai编程插件汇总iFlyCode、codegeex

1、iFlyCode 开发公司&#xff1a;讯飞 支持IDE: VS Code、IntelliJ IDEA、CLion、PyCharm、WebStorm 支持语言: Python、JavaScript、C、Java 下载地址&#xff1a;https://iflycode.xfyun.cn/ iFlyCode 快捷键列表&#xff1a;  Tab 采纳建议  Esc 拒绝建议  Alt\ 主动…

【NetCore】企业微信你消息推送

前言 慢慢脱离一线开发工作,突然接到一个改善提案需要增加类似短信业务消息提醒。考虑到目前已开通企业微信且短信服务一般需要收费,决定采用企业微信消息提醒。 流程 微信等相关的第三方开发大致流程都类似,如下 注册账号账号认证(为了权限,当然企业微信内部应用不需要…

【数据结构与算法】DFA算法-关键词匹配-java案例实现

该算法往往是用于匹配一些敏感词、绝对词等&#xff0c;从一篇文章中快速找到其中包含的关键词。 实现思路&#xff1a; 先读取所有关键词并存入set集合中。再将set中的关键词存入HashMap中&#xff0c;是以每个关键词字顺序存储&#xff0c;key为一个字、value为一个HashMap。…

机器视觉人体跌倒检测系统 - opencv python 计算机竞赛

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 机器视觉人体跌倒检测系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&…

frida hook(深入浅出)

Andrax搭建Android渗透测试环境 编译官方frida源码 APP基于Frida脱壳 Frida Hook的使用方法 Frida逆向分析APP实战 魔改frida到绕过检测的思路 配置fridaserver为后台进程 使用frida-net玩转frida-rpc 内置frida hook到Android系统 内置frida-inject工具到手机系统 三…

I/O控制器

一、概述 I/O设备管理-机械部件(I/O硬件设备)电子部件(I/O控制器)接口(设备与控制器间通信媒介) 二、组成 各寄存器间的编址方式&#xff1a; 三、控制方式 1.轮询程序控制 2.中断程序控制 3.直接存储器访问(DMA)

漏刻有时百度地图API实战开发(3)自动获取地图多边形中心点坐标

在百度地图中&#xff0c;多边形覆盖物表示地图上的多边形区域。这些区域可以是封闭的&#xff0c;也可以是开放的。多边形覆盖物通常用于表示地图上的行政区划、地标或任何需要强调的区域。 多边形覆盖物拥有自己的地理坐标&#xff0c;并能随着地图的拖动或缩放而相应地移动…

2023最新最全【Adobe After Effection 2023】下载安装零基础教程【附安装包】

AE2023下载点这里 教学 1.鼠标右击【Ae2023(64bit)】压缩包选择&#xff08;win11系统需先点击“显示更多选项”&#xff09;【解压到 Ae2023(64bit)】。 2.打开解压后的文件夹&#xff0c;鼠标右击【Set-up】选择【以管理员身份运行】。 3.点击【文件夹图标】&#xff0c;…

Scala爬虫如何实时采集天气数据?

这是一个基本的Scala爬虫程序&#xff0c;使用了Scala的http library来发送HTTP请求和获取网页内容。在爬取天气预报信息时&#xff0c;我们首先需要创建一个代理对象proxy&#xff0c;并将其用于发送HTTP请求。然后&#xff0c;我们使用http库的GET方法获取网页内容&#xff0…

Leetcode-LCR 021 删除链表的倒数第 N 个结点

快慢指针&#xff0c;快指针先移动n-1个节点后&#xff0c;慢指针从虚拟头结点出发&#xff08;相当于快慢指针相隔n个节点&#xff09;&#xff0c;快慢指针一起向链表尾依次移动一个结点&#xff0c;当快指针移动到表位时&#xff0c;慢指针正好移到被删除元素的前一个结点&a…

扭矩传感器采用一阶RC滤波

扭矩传感器内部的一阶RC&#xff08;电阻-电容&#xff09;滤波器是一种用于滤除高频噪声和干扰的电路&#xff0c;以确保传感器输出的信号平滑和稳定。这种滤波器通常用于传感器输出信号的处理&#xff0c;以减少测量中的高频噪声。 一阶RC滤波器通常由一个电阻&#xff08;R…

AM5-DB低压备自投装置在河北冠益荣信科技公司洞庭变电站工程中的应用

叶根胜 安科瑞电气股份有限公司 上海嘉定 201800 摘 要&#xff1a;随着电力需求的增加&#xff0c;电力系统对供电可靠性的要求越来越高&#xff0c;许多供电系统已经有两条或两条以上的供电线路。备用电源自动输入装置可有效提高供电可靠性&#xff0c;工作电源故障断开后可…

三菱FX3U系列—原点回归指令

目录 一、简介 二、指令形式 1、原点指令[ZRN/DZRN] 2、带搜索的原点回归指令[DSZR] 三、回归指令运行过程 1、ZRN原点回归运行过程 2、带搜索的原点回归运行过程 四、特殊辅助继电器 五、特殊输出模块 六、总结 一、简介 用于将电机或伺服驱动器控制的轴回到预定的原…

PHP利用jsonp跨域实现

JSONP&#xff08;JSON with Padding&#xff09;是一种使用普通的<script>标签来发起跨域请求的方法。JSONP利用了HTML页面可以直接引入外部JavaScript文件而不受同源策略限制的特性 实现效果 1、实现代码中php文件 <?php // 接收GET请求参数 $param $_GET[callb…

2023年双11有哪些便宜的云服务器值得推荐?

每年的双11期间各大云计算服务商都会推出特价云服务器&#xff0c;今年自然也不例外&#xff0c;下面给大家分享2023年双11有哪些便宜的云服务器值得推荐。 1、阿里云【传送门>>>】 阿里云双11推出了金秋云创季活动&#xff0c;2核2G3M不限流量&#xff0c;1年99元&…

springboot中如何同时操作同一功能

问题描述 测试阶段&#xff0c;由于存在某一功能的同时操作&#xff0c;该功能还是入库逻辑&#xff0c;此时若不进行处理&#xff0c;会造成插入表中多条重复数据&#xff0c;为此该问题需要修复。 解决办法 在接口开始进行对是否存在某个key值的判断&#xff0c;若不存在&…

【C++百宝箱】语法总结:命名空间 | 输入输出 | 缺省参数 | 函数重载

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;C入门宝典 &#x1f525;本文主要探讨C的语法&#xff0c;并深入了解C如何针对C语言中存在的不合理之处进行优化改进。 目录&#xff1a; ⌛…

遭受网络攻击泄露了101GB数据

臭名昭著的BlackCat/ALPHV勒索软件团伙声称对另一个组织发起了攻击。今天轮到意大利-法国科西嘉-费里斯公司发现自己正在与勒索软件作斗争。 BlackCat 在其数据泄露网站上报告称&#xff0c;该公司是网络攻击的受害者&#xff0c;并发布了从该公司 IT 基础设施中泄露的一系列样…

Apache Airflow Celery Broker 远程命令执行 (CVE-2020-11981)漏洞复现

漏洞描述 Apache Airflow 是一个开源的分布式任务调度框架。在 1.10.10 之前的版本中&#xff0c;如果 Redis 代理&#xff08;如 Redis 或 RabbitMQ&#xff09;已被攻击者控制&#xff0c;则攻击者可以在工作进程中执行任意命令。 漏洞环境及利用 搭建docker环境 要利用此…