<C++> STL_容器适配器

1.容器适配器

适配器是一种设计模式,该种模式是将一个类的接口转换成客户希望的另外一个接口。

容器适配器是STL中的一种重要组件,用于提供不同的数据结构接口,以满足特定的需求和限制。容器适配器是基于其他STL容器构建的,通过改变其接口和行为,使其适应不同的应用场景。

STL中有三种常见的容器适配器:stackqueuepriority_queue

虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque,比如:

在这里插入图片描述

在这里插入图片描述

而priority_queue默认使用vector

在这里插入图片描述

2.stack的介绍和使用

stack的介绍

  1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。

  2. stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。

  3. stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:

    • empty:判空操作

    • back:获取尾部元素操作

    • push_back:尾部插入元素操作

    • pop_back:尾部删除元素操作

  4. 标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况下使用deque。

在这里插入图片描述

stack的使用

当你使用 std::stack 时,你可以使用以下一些重要的成员函数:

  1. push(const T& value): 将元素 value 压入栈顶。
  2. pop(): 弹出栈顶元素,不返回其值。
  3. top(): 返回栈顶元素的引用,但不将其从栈中移除。
  4. size(): 返回栈中元素的数量。
  5. empty(): 检查栈是否为空,返回布尔值。
  6. swap(stack& other): 将当前栈与另一个栈 other 进行交换。
#include <iostream>
#include <stack>

int main() {
    std::stack<int> myStack;

    // 将元素压入栈顶
    myStack.push(10);
    myStack.push(20);
    myStack.push(30);

    std::cout << "栈顶元素: " << myStack.top() << std::endl; // 输出:30
    std::cout << "栈的大小: " << myStack.size() << std::endl; // 输出:3

    myStack.pop(); // 弹出栈顶元素

    if (!myStack.empty()) {
        std::cout << "弹出元素后的栈顶元素: " << myStack.top() << std::endl; // 输出:20
    }

    std::stack<int> anotherStack;
    anotherStack.push(50);

    myStack.swap(anotherStack); // 交换两个栈的内容

    std::cout << "交换后 myStack 的大小: " << myStack.size() << std::endl; // 输出:1

    return 0;
}

stack的模拟实现

#pragma once
#include <deque>

namespace phw {
    template<class T, class Container = std::deque<T>>
    class stack {
    public:
        void push(const T &x) {
            _con.push_back(x);
        }

        void pop() {
            _con.pop_back();
        }

        T &top() {
            return _con.back();
        }

        const T &top() const {
            return _con.back();
        }

        size_t size() const {
            return _con.size();
        }

        bool empty() const {
            return _con.empty();
        }

        void swap(stack<T, Container> &st) {
            _con.swap(st._con);
        }

    private:
        Container _con;
    };
}// namespace phw

3.queue的介绍和使用

queue的介绍

  1. 队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端 提取元素。

  2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的 成员函数来访问其元素。元素从队尾入队列,从队头出队列。

  3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:

    • empty:检测队列是否为空

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

    • front:返回队头元素的引用

    • back:返回队尾元素的引用

    • push_back:在队列尾部入队列

    • pop_front:在队列头部出队列

  4. 标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标 准容器deque。

queue的使用

  1. push(const T& value): 将元素 value 添加到队列的尾部。
  2. pop(): 弹出队列的头部元素,不返回其值。
  3. front(): 返回队列头部元素的引用。
  4. back(): 返回队列尾部元素的引用。
  5. size(): 返回队列中元素的数量。
  6. empty(): 检查队列是否为空。
  7. swap(queue& other): 交换两个队列的内容。

示例:

#include <iostream>
#include <queue>

int main() {
    std::queue<int> myQueue;

    // 向队列尾部添加元素
    myQueue.push(10);
    myQueue.push(20);
    myQueue.push(30);

    std::cout << "队列头部元素: " << myQueue.front() << std::endl; // 输出:10
    std::cout << "队列尾部元素: " << myQueue.back() << std::endl;  // 输出:30
    std::cout << "队列大小: " << myQueue.size() << std::endl;     // 输出:3

    myQueue.pop(); // 弹出队列头部元素

    std::cout << "弹出元素后的队列头部元素: " << myQueue.front() << std::endl; // 输出:20

    std::queue<int> anotherQueue;
    anotherQueue.push(50);

    myQueue.swap(anotherQueue); // 交换两个队列的内容

    std::cout << "交换后 myQueue 的大小: " << myQueue.size() << std::endl; // 输出:1

    return 0;
}

queue的模拟实现

#pragma once
#include <deque>

namespace phw//防止命名冲突
{
    template<class T, class Container = std::deque<T>>
    class queue {
    public:
        //队尾入队列
        void push(const T &x) {
            _con.push_back(x);
        }

        //队头出队列
        void pop() {
            _con.pop_front();
        }

        //获取队头元素
        T &front() {
            return _con.front();
        }

        const T &front() const {
            return _con.front();
        }

        //获取队尾元素
        T &back() {
            return _con.back();
        }

        const T &back() const {
            return _con.back();
        }

        //获取队列中有效元素个数
        size_t size() const {
            return _con.size();
        }

        //判断队列是否为空
        bool empty() const {
            return _con.empty();
        }

        //交换两个队列中的数据
        void swap(queue<T, Container> &q) {
            _con.swap(q._con);
        }

    private:
        Container _con;
    };
}// namespace phw

4.priority_queue的介绍和使用

priority_queue的介绍

std::priority_queue 用于实现优先队列数据结构。优先队列是一种特殊的队列,其中的元素按照一定的优先级顺序进行排列,而不是按照插入的顺序。

std::priority_queue 使用堆(heap)数据结构来维护元素的顺序,通常用于需要按照一定规则获取最高优先级元素的场景。在默认情况下,std::priority_queue 会以降序排列元素,即最大的元素会被放置在队列的前面。你也可以通过提供自定义的比较函数来改变排序顺序。

priority_queue的使用

构造函数

默认构造函数: 创建一个空的优先队列。

std::priority_queue<T> pq;  // 创建一个空的优先队列,T 是元素类型

带有比较函数的构造函数: 可以指定一个自定义的比较函数,用于确定元素的优先级顺序。

std::priority_queue<T, Container, Compare> pq;
  • T 是元素类型。
  • Container 是底层容器类型,默认情况下使用 std::vector
  • Compare 是比较函数类型,用于确定元素的顺序。默认情况下,使用 operator< 来比较元素。

示例:

#include <iostream>
#include <queue>

int main() {
    // 构造一个默认的优先队列,元素类型为 int,使用默认比较函数 operator<
    std::priority_queue<int> pq1;

    // 构造一个优先队列,元素类型为 int,使用自定义的比较函数 greater,实现最小堆
    std::priority_queue<int, std::vector<int>, std::greater<int>> pq2;

    // 构造一个优先队列,元素类型为 std::string,使用默认比较函数 operator<
    std::priority_queue<std::string> pq3;

    // 构造一个优先队列,元素类型为 double,使用 lambda 表达式作为比较函数,实现最大堆
    auto cmp = [](double a, double b) { return a < b; };
    std::priority_queue<double, std::vector<double>, decltype(cmp)> pq4(cmp);

    return 0;
}

成员函数

  1. push(): 向优先队列中插入元素。

    pq.push(value);  // 插入元素 value
    
  2. pop(): 移除队列顶部的元素(即最高优先级元素)。

    pq.pop();  // 移除队列顶部元素
    
  3. top(): 获取队列顶部的元素(即最高优先级元素)。

    T highestPriority = pq.top();  // 获取最高优先级元素
    
  4. empty(): 检查队列是否为空。

    bool isEmpty = pq.empty();  // 如果队列为空则返回 true,否则返回 false
    
  5. size(): 获取队列中的元素数量。

    int numElements = pq.size();  // 获取队列中的元素数量
    

示例:

#include <iostream>
#include <functional>
#include <queue>
using namespace std;
int main(){
	priority_queue<int> q;
	q.push(3);
	q.push(6);
	q.push(0);
	q.push(2);
	q.push(9);
	q.push(8);
	q.push(1);
	while (!q.empty())
	{
		cout << q.top() << " ";
		q.pop();
	}
	cout << endl; //9 8 6 3 2 1 0
	return 0;
}

自定义比较函数

你可以通过提供自定义的比较函数来改变元素的排序顺序。比较函数应该返回 true,如果第一个参数应该排在第二个参数之前。

struct MyComparator {
    bool operator()(const T& a, const T& b) {
        // 自定义比较逻辑
    }
};

std::priority_queue<T, Container, MyComparator> pq;

priority_queue的模拟实现

priority_queue的底层实际上就是堆结构,实现priority_queue之前,我们先认识两个重要的堆算法。(下面这两种算法我们均以大堆为例)

堆的向上调整算法

在这里插入图片描述

以大堆为例,堆的向上调整算法就是在大堆的末尾插入一个数据后,经过一系列的调整,使其仍然是一个大堆。

调整的基本思想如下:

1、将目标结点与其父结点进行比较。
2、若目标结点的值比父结点的值大,则交换目标结点与其父结点的位置,并将原目标结点的父结点当作新的目标结点继续进行向上调整;若目标结点的值比其父结点的值小,则停止向上调整,此时该树已经是大堆了。

例如,现在我们在该大堆的末尾插入数据88。

在这里插入图片描述

我们先将88与其父结点54进行比较,发现88比其父结点大,则交换父子结点的数据,并继续进行向上调整。

在这里插入图片描述

此时将88与其父结点87进行比较,发现88还是比其父结点大,则继续交换父子结点的数据,并继续进行向上调整。

在这里插入图片描述

这时再将88与其父结点89进行比较,发现88比其父结点小,则停止向上调整,此时该树已经就是大堆了。

在这里插入图片描述

堆的向上调整算法代码:

void AdjustUp(int child) {
    int parent = (child - 1) / 2;
    while (child > 0) {
        // 默认less ,也就是parent<child
        if (_comp(_con[parent], _con[child]))// 通过所给比较方式确定是否需要交换结点位置
        {
            // 将父结点与孩子结点交换
            swap(_con[child], _con[parent]);
            // 继续向上进行调整
            child = parent;
            parent = (child - 1) / 2;
        } else// 已成堆
        {
            break;
        }
    }
}

堆的向下调整算法

以大堆为例,使用堆的向下调整算法有一个前提,就是待向下调整的结点的左子树和右子树必须都为大堆。

在这里插入图片描述

调整的基本思想如下:

1、将目标结点与其较大的子结点进行比较。
2、若目标结点的值比其较大的子结点的值小,则交换目标结点与其较大的子结点的位置,并将原目标结点的较大子结点当作新的目标结点继续进行向下调整;若目标结点的值比其较大子结点的值大,则停止向下调整,此时该树已经是大堆了。

例如,将该二叉树从根结点开始进行向下调整。(此时根结点的左右子树已经是大堆)

在这里插入图片描述

将60与其较大的子结点88进行比较,发现60比其较大的子结点小,则交换这两个结点的数据,并继续进行向下调整。

在这里插入图片描述

此时再将60与其较大的子结点87进行比较,发现60比其较大的子结点小,则再交换这两个结点的数据,并继续进行向下调整。

在这里插入图片描述

这时再将60与其较大的子结点54进行比较,发现60比其较大的子结点大,则停止向下调整,此时该树已经就是大堆了。

在这里插入图片描述

堆的向下调整算法代码:

void AdjustDown(int n, int parent) {
    int child = 2 * parent + 1;
    while (child < n) {
        //_comp(_con[child], _con[child + 1])表示child<child+1
        if (child + 1 < n && _comp(_con[child], _con[child + 1])) {
            child++;
        }
        // parent<child
        if (_comp(_con[parent], _con[child]))// 通过所给比较方式确定是否需要交换结点位置
        {
            // 将父结点与孩子结点交换
            swap(_con[child], _con[parent]);
            // 继续向下进行调整
            parent = child;
            child = 2 * parent + 1;
        } else// 已成堆
        {
            break;
        }
    }
}

模拟实现代码:

// 优先级队列使用vector作为其底层存储数据的容器,priority_queue就是堆
// 默认情况是大堆

#include <iostream>
#include <vector>
using namespace std;
namespace phw {
    // 仿函数less(使内部结构为大堆)
    template<class T>
    struct less {
        bool operator()(const T &x, const T &y) {
            return x < y;
        }
    };

    // 仿函数greater(使内部结构为小堆)
    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 = greater<T>>
    class priority_queue {
    public:
        // 堆的向上调整
        void AdjustUp(int child) {
            int parent = (child - 1) / 2;
            while (child > 0) {
                // 默认less ,也就是parent<child
                if (_comp(_con[parent], _con[child]))// 通过所给比较方式确定是否需要交换结点位置
                {
                    // 将父结点与孩子结点交换
                    swap(_con[child], _con[parent]);
                    // 继续向上进行调整
                    child = parent;
                    parent = (child - 1) / 2;
                } else// 已成堆
                {
                    break;
                }
            }
        }
        // 堆的向下调整
        void AdjustDown(int n, int parent) {
            int child = 2 * parent + 1;
            while (child < n) {
                //_comp(_con[child], _con[child + 1])表示child<child+1
                if (child + 1 < n && _comp(_con[child], _con[child + 1])) {
                    child++;
                }
                // parent<child
                if (_comp(_con[parent], _con[child]))// 通过所给比较方式确定是否需要交换结点位置
                {
                    // 将父结点与孩子结点交换
                    swap(_con[child], _con[parent]);
                    // 继续向下进行调整
                    parent = child;
                    child = 2 * parent + 1;
                } else// 已成堆
                {
                    break;
                }
            }
        }

        // 插入元素到队尾(并排序)
        void push(const T &x) {
            _con.push_back(x);
            AdjustUp(_con.size() - 1);// 将最后一个元素进行一次向上调整
        }

        // 弹出队头元素(堆顶元素)
        void pop() {
            swap(_con[0], _con[_con.size() - 1]);
            _con.pop_back();
            AdjustDown(_con.size(), 0);// 将第0个元素进行一次向下调整
        }

        // 访问队头元素(堆顶元素)
        T &top() {
            return _con[0];
        }
        const T &top() const {
            return _con[0];
        }
        // 获取队列中有效元素个数
        size_t size() const {
            return _con.size();
        }
        // 判断队列是否为空
        bool empty() const {
            return _con.empty();
        }

    private:
        Container _con;// vector容器
        Compare _comp; // 比较方式
    };
}// namespace phw
(_con[0], _con[_con.size() - 1]);
            _con.pop_back();
            AdjustDown(_con.size(), 0);// 将第0个元素进行一次向下调整
        }

        // 访问队头元素(堆顶元素)
        T &top() {
            return _con[0];
        }
        const T &top() const {
            return _con[0];
        }
        // 获取队列中有效元素个数
        size_t size() const {
            return _con.size();
        }
        // 判断队列是否为空
        bool empty() const {
            return _con.empty();
        }

    private:
        Container _con;// vector容器
        Compare _comp; // 比较方式
    };
}// namespace phw

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【VRTK4.0运动专题】轴移动AxisMove(真实身体的移动)

文章目录 1、概览2、释义3、属性设置 1、概览 2、释义 “竖直轴”控制的行为“水平轴”控制的行为1Vertical-Slide 滑动Horizontal-Slide 滑动2Vertical-Slide 滑动Horizontal-SmoothRotate 转动3Vertical-Slide 滑动Horizontal-SnapRotate 转动&#xff08;不连续&#xff09…

openGauss学习笔记-47 openGauss 高级数据管理-权限

文章目录 openGauss学习笔记-47 openGauss 高级数据管理-权限47.1 语法格式47.2 参数说明47.3 示例 openGauss学习笔记-47 openGauss 高级数据管理-权限 数据库对象创建后&#xff0c;进行对象创建的用户就是该对象的所有者。数据库安装后的默认情况下&#xff0c;未开启三权分…

java八股文面试[数据结构]——HashMap和HashTable区别

HashMap源码中的重要常量 DEFAULT_INITIAL_CAPACITY: HashMap的默认容量&#xff0c;16 MAXIMUM_CAPACITY&#xff1a; HashMap的最大支持容量&#xff0c;2^30 TREEIFY_THRESHOLD&#xff1a;Bucket中链表长度大于该默认值&#xff0c;转化为红黑树。 UNTREEIFY_THRESHOLD…

【微服务】微服务调用原理及服务治理

本文通过图文结合&#xff0c;简要讲述微服务的调用原理&#xff0c;以及服务治理的相关概念。 1.微服务的调用原理 举个栗子&#xff1a;你去会所洗脚。首先&#xff0c;技师肯定要先去会所应聘&#xff0c;通过之后&#xff0c;会所会记录该技师的信息和技能&#xff0c;然后…

BMP图片读写实践:rgb转bgr

本实理论上支持24位图和32位图&#xff0c;实际上只测试了24位。原理很简单&#xff0c;就是RGB中的蓝色字节和红色字节交换。 测试代码1&#xff1a; #include <stdio.h> #include <unistd.h> #include <sys/stat.h> #include <stdlib.h> #include &l…

回归预测 | MATLAB实现TSO-ELM金枪鱼群优化算法优化极限学习机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现TSO-ELM金枪鱼群优化算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现TSO-ELM金枪鱼群优化算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效…

初试Eureka注册中心

Eureka是spring cloud中的一个负责服务注册与发现的组件。遵循着CAP理论中的A(可用性)P(分区容错性)。一个Eureka中分为eureka server和eureka client。其中eureka server是作为服务的注册与发现中心。 搭建eureka服务 引入eureka依赖 引入SpringCloud为eureka提供的starter依…

橙河:海外賺美金的项目有哪些?

大家好&#xff0c;我是橙河老师。现在呢&#xff0c;很多人去国外打工&#xff0c;大家在短视频上也经常能看到&#xff0c;他们在国外挣了几年钱&#xff0c;回来就能买车买房。 其实呢&#xff0c;他们在国外的工作&#xff0c;工资也就是几千块一个月&#xff0c;不过他们…

C语言数值表示——进制、数值存储方式

进制 进制也就是进位制&#xff0c;是人们规定的一种进位方法对于任何一种进制—X进制&#xff0c;就表示某一位置上的数运算时是逢X进一位 十进制是逢十进一&#xff0c;十六进制是逢十六进一&#xff0c;二进制就是逢二进一&#xff0c;以此类推&#xff0c;x进制就是逢x进位…

通俗理解DDPM到Stable Diffusion原理

代码1&#xff1a;stabel diffusion 代码库代码2&#xff1a;diffusers 代码库论文&#xff1a;High-Resolution Image Synthesis with Latent Diffusion Models模型权重&#xff1a;runwayml/stable-diffusion-v1-5 文章目录 1. DDPM的通俗理解1.1 DDPM的目的1.2 扩散过程1.3 …

编写Dockerfile制作Web应用系统nginx镜像,生成镜像nginx:v1.1,并推送其到私有仓库。

环境&#xff1a; CentOS 7 Linux 3.10.0-1160.el7.x86_64 具体要求如下&#xff1a; &#xff08;1&#xff09;基于centos基础镜像&#xff1b; &#xff08;2&#xff09;指定作者信息&#xff1b; &#xff08;3&#xff09;安装nginx服务&#xff0c;将提供的dest目录…

Linux学习之DNS服务的原理

DNS服务一些理论 域名系统&#xff08;Domain Name System&#xff0c;DNS&#xff09;是互联网的核心应用服务&#xff0c;可以通过IP地址查询到域名&#xff0c;也可以通过域名查询到IP地址。 FQDN&#xff08;Full Qualified Domain Name&#xff09;是完全限定域名&#xf…

Apache Paimon 实时数据湖 Streaming Lakehouse 的存储底座

摘要&#xff1a;本文整理自阿里云开源大数据表存储团队负责人&#xff0c;阿里巴巴高级技术专家李劲松&#xff08;之信&#xff09;&#xff0c;在 Streaming Lakehouse Meetup 的分享。内容主要分为四个部分&#xff1a; 流计算邂逅数据湖 Paimon CDC 实时入湖 Paimon 不止…

java八股文面试[多线程]——死锁、活锁、饥饿

DCL双重锁&#xff1a;TODO 如何预防死锁&#xff1a; 如何查看线程死锁&#xff1a; 知识来源&#xff1a; 【2023年面试】描述一下线程安全活跃态问题&#xff0c;以及竞态条件_哔哩哔哩_bilibili 【2023年面试】如何预防死锁_哔哩哔哩_bilibili 【并发与线程】阿里一面&…

element Collapse 折叠面板 绑定事件

1. 点击面板触发事件 change <el-collapse accordion v-model"activeNames" change"handleChange"><el-collapse-item title"一致性 Consistency"><div>与现实生活一致&#xff1a;与现实生活的流程、逻辑保持一致&#xff0c…

迅为RK3588开发板Android12 设置系统默认不锁屏

修改 frameworks/base/packages/SettingsProvider/res/values/defaults.xml 文件&#xff0c;修改为如下 所示&#xff1a; - <bool name"def_lockscreen_disabled">false</bool> <bool name"def_lockscreen_disabled">true</bool&…

怎么建设ITIIL运维管理体系?

市场上大多数ITIL解决方案都过于复杂&#xff0c;让我们举一个客户希望实施ITIL方案的例子。首先&#xff0c;客户要通过ITIL咨询来定义ITIL流程&#xff0c;并使其与业务目标保持一致。接下来就是购买ITIL软件&#xff1b;大多数ITIL解决方案将事件、问题和变更管理作为不同的…

一生一芯9——ubuntu22.04安装valgrind

这里安装的valgrind版本是3.19.0 下载安装包 在选定的目录下打开终端&#xff0c;输入以下指令 wget https://sourceware.org/pub/valgrind/valgrind-3.19.0.tar.bz2直至下载完成 解压安装包 输入下面指令解压安装包 tar -xvf valgrind-3.19.0.tar.bz2.tar.bz2注&#xf…

Jmeter(二十八):beanshell的使用

Beanshell 是一种轻量级的 Java 脚本,纯 Java 编写的,能够动态的执行标准 java 语法及一些扩展脚本语法,类似于 javaScript,在工作中可能用的多的就是: Beanshell 取样器:跟Http取样器并列Beanshell前置处理器:一般放在Http请求下,在请求前处理一些数据Beanshell后置处…

1.6 服务器处理客户端请求

客户端进程向服务器进程发送一段文本&#xff08;MySQL语句&#xff09;&#xff0c;服务器进程处理后再向客户端进程发送一段文本&#xff08;处理结果&#xff09;。 从图中我们可以看出&#xff0c;服务器程序处理来自客户端的查询请求大致需要经过三个部分&#xff0c;分别…