C++学习指南(七)——stack/queue/priority_queue

欢迎来到繁星的CSDN,本期内容主要包括stack(栈),queue(队列),priority_queue(堆)

实际上,C++中的stack、queue还有priority_queue与C语言中的内容无异,所以本篇文章主要想去阐述的,是有关stack、queue还有priority_queue的一些接口与实现原理。

一、deque

在说明栈、队列以及优先队列是如何实现之前,我们必须阐述deque的相关知识。

deque是什么?为什么要有deque?以及deque在模拟实现中扮演着什么样的角色?

当我们翻到stl库里的stack和queue的头文件时(只需要在VS2022所在盘搜索stl_queue.h和stl_stack.h即能得到其头文件,这边推荐使用everything软件辅助快速寻找)我们可以看到Sequence的缺省值是deque<_Tp>,Sequence在后续的代码中不难发现是起容器的作用,而_Tp则扮演了stack内存储值的类型。

deque是什么以及为什么用deque

deque实际上是vector与list的结合体,由多个vector组成,并以list的形式串连起来。

这么做的好处是什么?

让我们回想stack和queue的特点,一个是先进先出,一个是后进先出。如果都用vector来储存的话,那么stack需要的是pop_back(),queue需要的是pop_front()。但是用vector来pop_back简单,pop_front就难上加难了,用list的话首尾删除插入倒是方便,但是在存储空间上由于存储的地址不连续性,以及list每个结点所需要存储的信息更多,使得整体耗费的存储空间更大,且首插尾插效率很低。

于是deque,一个结合了vector与list的东西诞生了。

很可惜的是,事实上deque结合了vector和list的优点,更结合了vector与list的缺点。进行尾删操作尚且简单,如果进行头删则需要将其他位置所有的数据向前搬一格。如果进行插入操作,则每次插入都有可能多一个同样长度的vector,一插一删之间,deque很可能造成大量的空间浪费,亦或是牺牲了"搬迁"的效率。

或许在平均效率上deque比单纯用vector或者list更高吧,但stl创建者选用deque的原因我们不得而知。

二、stack和queue的接口

stack和queue的接口是十分类似的。

cpp 复制代码
push(x);
pop();
empty();
size();
top();//stack
front();//queue
back();//queue

与string的区别是,string是push_back,但stack和queue是push。string是pop_back,但stack和queue是pop。queue的front和back分别代表第一个元素以及最新被push的元素。stack的top表示栈顶,没有栈底接口。

为什么要坚持用接口?

这一点实际上在封装的那一部分已经被提到过了。我们用接口的目的一方面是为了更简单地使用这些复杂的数据结构,一方面是为了数据的安全性,我们只能使用接口对这些结构进行改造,而不能直接从底层的vector或者deque改数据,这种操作对于结构而言是"不合法"的(对于程序而言并没有什么问题)。

三、priority_queue

优先队列(priority_queue)实际上是堆,其优先性区分了大根堆与小根堆,但值得注意的是,如果需要使用priority_queue,我们需要包的头文件是<queue>。

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

默认的优先队列是大根堆,在日常使用中我们仅仅需要输入堆所存储的数据类型即可。如果需要小根堆,那么就需要输入数据类型,存储容器(一般为vector),以及std::greater<>(递增)。或者自己书写仿函数。

优先队列的常用接口如下:

cpp 复制代码
push(x);
pop();
top();
empty();
size();

其中push放入后,优先队列会自动进行排序,top即堆的根,pop即弹出堆的根,size和empty就不过多赘述了。

下面是大致的模拟实现。

cpp 复制代码
#pragma once
#include<deque>
template<class T,class Con = std::deque<T>>
class stack
{
public:
    stack();
    void push(const T& x) {
        _c.push_back(x);
    }
    void pop() {
        _c.pop_back();
    }
    T& top() {
        return _c.back();
    }
    const T& top()const {
        return _c.back();
    }
    size_t size()const {
        return _c.back();
    }
    bool empty()const {
        return _c.empty();
    }
private:
    Con _c;
};
cpp 复制代码
#pragma once
#include<deque>
template<class T, class Con = std::deque<T>>

class queue
{
public:
    queue();
    void push(const T& x) {
        _c.push_back(x);
    }
    void pop() {
        _c.pop_front();
    }
    T& back() {
        return _c.back();
    }
    const T& back()const {
        return _c.back();
    }
    T& front() {
        return _c.front();
    }
    const T& front()const {
        return _c.front();
    }
    size_t size()const {
        return _c.size();
    }
    bool empty()const {
        return _c.empty();
    }
private:
    Con _c;
};
cpp 复制代码
#pragma once
namespace bit
{
#include<vector>
#include<functional>
    template <class T, class Container = std::vector<T>, class Compare = std::less<T> >
    class priority_queue
    {
    public:
        priority_queue();
        template <class InputIterator>
        priority_queue(InputIterator first, InputIterator last) {
            std::make_heap(first,last,comp);
        }
        bool empty() const {
            return c.empty();
        }
        size_t size() const {
            return c.size();
        }
        T& top() const {
            return c.front();
        }
        void push(const T& x) {
            x.push_back(x);
            std::push_heap(c.begin(), c.end(), comp);
        }
        void pop() {
            std::pop_heap(c.begin(), c.end(), comp);
            c.pop_back();
        }
    private:
        Container c;
        Compare comp;
    };
};
相关推荐
lsx20240610 分钟前
Perl 面向对象编程指南
开发语言
Allen Bright30 分钟前
【Java基础-46.3】Java泛型通配符详解:解锁类型安全的灵活编程
java·开发语言
JANGHIGH37 分钟前
c++ std::list使用笔记
c++·笔记·list
画个逗号给明天"44 分钟前
C++STL容器之list
开发语言·c++
hrrrrb1 小时前
【Java】Java 常用核心类篇 —— 时间-日期API(上)
java·开发语言
小突突突1 小时前
模拟实现Java中的计时器
java·开发语言·后端·java-ee
七禾页话1 小时前
垃圾回收知识点
java·开发语言·jvm
web137656076431 小时前
Scala的宝藏库:探索常用的第三方库及其应用
开发语言·后端·scala
煤炭里de黑猫2 小时前
Lua C API :使用 lua_tonumber 函数从 Lua 栈中提取数值
开发语言·lua
闲猫2 小时前
go 反射 interface{} 判断类型 获取值 设置值 指针才可以设置值
开发语言·后端·golang·反射