stack和queue的基本知识

(写给未来遗忘的自己)

stack 

 在 C++ 中,stack 是标准模板库(STL)的一部分,是一种容器适配器。容器适配器为现有的容器提供了一种特定的接口,而不改变底层容器的实现方式。stack 是一个典型的后进先出(LIFO,Last In First Out)数据结构。

基本特性

  • 容器适配器stack 是一个容器适配器,默认情况下使用 deque 作为底层容器,但可以使用任何符合要求的容器,如 vectorlist
  • 接口stack 提供了一组有限的接口,以限制对底层容器的访问方式。这些接口包括 pushpoptopemptysize

stack 的常用方法

  1. push(const T& value):向栈顶添加一个元素。
  2. pop():移除栈顶的元素。
  3. top():返回栈顶的元素。
  4. empty():检查栈是否为空。
  5. size():返回栈中元素的数量。
#include <iostream>
#include <stack>
#include <vector>
#include <list>

int main() {
    std::stack<int> s; // 使用默认的 deque 作为底层容器
    std::stack<int, std::vector<int>> s2; // 使用 vector 作为底层容器
    std::stack<int, std::list<int>> s3; // 使用 list 作为底层容器

    s.push(1);
    s.push(2);
    s.push(3);

    while (!s.empty()) {
        std::cout << s.top() << " "; // 输出栈顶元素
        s.pop(); // 移除栈顶元素
    }

/**************************************************************************************/
     // top:返回栈顶元素
    std::cout << "Top element: " << s.top() << std::endl;  // 输出 3

    // pop:移除栈顶元素
    s.pop();
    std::cout << "Top element after pop: " << s.top() << std::endl;  // 输出 2

    // size:返回栈中元素的数量
    std::cout << "Stack size: " << s.size() << std::endl;  // 输出 2

    // empty:检查栈是否为空
    std::cout << "Is stack empty? " << (s.empty() ? "Yes" : "No") << std::endl;  // 输出 No

    // 继续 pop 直到栈为空
    s.pop();
    s.pop();
    std::cout << "Is stack empty after popping all elements? " << (s.empty() ? "Yes" : "No") << std::endl;  // 输出 Yes

    return 0;
}

 

stack 的实现

stack 是通过对底层容器的接口进行包装和限制来实现的。以下是一个简化的实现示例:

#include <deque>

template <typename T, typename Container = std::deque<T>>
class Stack {
public:
    void push(const T& value) {
        container.push_back(value);
    }

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

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

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

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

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

private:
    Container container;
};

附:自定义模板的解释

template <typename T, typename Container = std::deque<T>>

/*
1.template <typename T, typename Container = std::deque<T>>:

template <typename T, typename Container = std::deque<T>> 
声明了一个模板,其中 T 是类型参数,Container 是一个模板参数,
其默认类型是 std::deque<T>。这意味着在没有指定 Container 类型时,
默认使用 std::deque<T> 作为 Container 类型。


2.typename T:

T 是一个模板类型参数,表示可以传递任何类型。


3.typename Container = std::deque<T>:

Container 是另一个模板类型参数,它有一个默认值 std::deque<T>。
std::deque<T> 是一个标准库的双端队列(deque),它是 Container 的默认类型。
这样一来,如果用户在实例化模板时不指定 Container 类型,模板将使用 
std::deque<T> 作为默认的容器类型。


*/

stack 不提供迭代器

stack 不提供迭代器,因为它的设计是为了隐藏底层容器的细节,并且只提供与栈操作相关的方法。迭代器允许对容器的任意访问,这与栈的 LIFO 特性不符。

如果需要遍历 stack 中的元素,应该使用底层容器。(一般不建议使用)

#include <stack>
#include <vector>
#include <iostream>

int main() {
    std::stack<int, std::vector<int>> s;
    s.push(1);
    s.push(2);
    s.push(3);

    std::vector<int>& underlyingContainer = const_cast<std::vector<int>&>(s._Get_container());

    for (auto it = underlyingContainer.begin(); it != underlyingContainer.end(); ++it) {
        std::cout << *it << " ";
    }

    return 0;
}

queue

std::queue 也是 C++ 标准模板库(STL)中的一个容器适配器,用于实现先进先出(FIFO,First In First Out)数据结构。与 std::stack 类似,std::queue 也依赖于底层容器来存储元素,但它对外只暴露队列操作的接口。

queue常用方法

  • push(const T& value):向队列尾部添加元素。
  • pop():移除队列头部的元素。
  • front():返回队列头部的元素。
  • back():返回队列尾部的元素。
  • empty():检查队列是否为空。
  • size():返回队列中元素的数量。

 

#include <iostream>
#include <queue>
#include <deque>
#include <list>

int main() {
    // 使用默认的 std::deque 作为底层容器
    std::queue<int> q;

    // push:向队列尾部添加元素
    q.push(1);
    q.push(2);
    q.push(3);

    // front:返回队列头部的元素
    std::cout << "Front element: " << q.front() << std::endl;  // 输出 1

    // back:返回队列尾部的元素
    std::cout << "Back element: " << q.back() << std::endl;  // 输出 3

    // pop:移除队列头部的元素
    q.pop();
    std::cout << "Front element after pop: " << q.front() << std::endl;  // 输出 2

    // size:返回队列中元素的数量
    std::cout << "Queue size: " << q.size() << std::endl;  // 输出 2

    // empty:检查队列是否为空
    std::cout << "Is queue empty? " << (q.empty() ? "Yes" : "No") << std::endl;  // 输出 No

    return 0;
}

自定义底层容器

你可以使用 std::liststd::vector 作为 queue 的底层容器,只要它们支持必要的操作

#include <iostream>
#include <queue>
#include <list>

int main() {
    std::queue<int, std::list<int>> q;

    q.push(1);
    q.push(2);
    q.push(3);

    std::cout << "Front element: " << q.front() << std::endl;  // 输出 1
    std::cout << "Back element: " << q.back() << std::endl;  // 输出 3

    q.pop();
    std::cout << "Front element after pop: " << q.front() << std::endl;  // 输出 2
    std::cout << "Queue size: " << q.size() << std::endl;  // 输出 2
    std::cout << "Is queue empty? " << (q.empty() ? "Yes" : "No") << std::endl;  // 输出 No

    return 0;
}

queue 的实现

#include <iostream>
#include <queue>
#include <deque>
#include <list>

// 自定义 queue 类,公开底层容器的访问
template <typename T, typename Container = std::deque<T>>
class MyQueue {
public:
    using value_type = T;
    using container_type = Container;
    using size_type = typename Container::size_type;

    explicit MyQueue(const Container& cont = Container()) : c(cont) {}
    explicit MyQueue(Container&& cont = Container()) : c(std::move(cont)) {}
    MyQueue(const MyQueue& other) : c(other.c) {}
    MyQueue(MyQueue&& other) noexcept : c(std::move(other.c)) {}

    MyQueue& operator=(const MyQueue& other) {
        c = other.c;
        return *this;
    }

    MyQueue& operator=(MyQueue&& other) noexcept {
        c = std::move(other.c);
        return *this;
    }

    bool empty() const { return c.empty(); }
    size_type size() const { return c.size(); }
    value_type& front() { return c.front(); }
    const value_type& front() const { return c.front(); }
    value_type& back() { return c.back(); }
    const value_type& back() const { return c.back(); }
    void push(const value_type& value) { c.push_back(value); }
    void push(value_type&& value) { c.push_back(std::move(value)); }
    void pop() { c.pop_front(); }

    // 公开底层容器的访问
    Container& get_container() { return c; }
    const Container& get_container() const { return c; }

private:
    Container c;
};

int main() {
    // 使用自定义的 queue 类,底层容器为 std::deque
    MyQueue<int> q;
    q.push(1);
    q.push(2);
    q.push(3);

    // 获取底层容器的引用
    std::deque<int>& underlyingContainer = q.get_container();

    // 遍历底层容器
    for (const int& elem : underlyingContainer) {
        std::cout << elem << " ";
    }

    return 0;
}

 不同底层实现的区别

std::queuestd::stack 是容器适配器,它们可以使用不同的底层容器实现来存储元素。选择不同的底层容器会影响到这些适配器的性能和特性。主要的底层容器包括 std::dequestd::vectorstd::list

1. std::deque

std::deque(双端队列)是 std::queuestd::stack 的默认底层容器,因为它提供了在头部和尾部的高效插入和删除操作。

  • 优点

    • 支持在头部和尾部进行常数时间的插入和删除操作。
    • 动态调整大小,不需要额外的内存拷贝。
    • 支持随机访问元素。
  • 缺点

    • 内部实现比较复杂,可能会占用更多的内存。
    • 相比 std::vector 在元素访问和遍历上稍慢。
#include <iostream>
#include <queue>
#include <deque>

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

    q.push(1);
    q.push(2);
    q.push(3);

    std::cout << "Front element: " << q.front() << std::endl;  // 输出 1
    std::cout << "Back element: " << q.back() << std::endl;  // 输出 3

    q.pop();
    std::cout << "Front element after pop: " << q.front() << std::endl;  // 输出 2

    return 0;
}

 

2. std::vector

std::vector 是动态数组,支持快速的随机访问。

  • 优点

    • 提供常数时间的随机访问。
    • 适合在尾部进行插入和删除操作。
    • 内存布局连续,有利于缓存局部性。
  • 缺点

    • 在头部插入和删除元素时效率较低,因为需要移动所有元素。
    • 当重新分配内存时,可能需要将整个数组复制到新位置。
#include <iostream>
#include <stack>
#include <vector>

int main() {
    std::stack<int, std::vector<int>> s;

    s.push(1);
    s.push(2);
    s.push(3);

    std::cout << "Top element: " << s.top() << std::endl;  // 输出 3

    s.pop();
    std::cout << "Top element after pop: " << s.top() << std::endl;  // 输出 2

    return 0;
}

3. std::list

std::list 是一个双向链表。

  • 优点

    • 支持在任何位置进行常数时间的插入和删除操作。
    • 不需要移动其他元素。
  • 缺点

    • 不支持随机访问,需要线性时间进行元素访问。
    • 内存开销较大,因为每个元素都需要存储指向前后元素的指针。
    • 相比 std::vectorstd::deque,遍历速度较慢。
#include <iostream>
#include <queue>
#include <list>

int main() {
    std::queue<int, std::list<int>> q;

    q.push(1);
    q.push(2);
    q.push(3);

    std::cout << "Front element: " << q.front() << std::endl;  // 输出 1
    std::cout << "Back element: " << q.back() << std::endl;  // 输出 3

    q.pop();
    std::cout << "Front element after pop: " << q.front() << std::endl;  // 输出 2

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值