移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——8.stack&&queue&&priority_queue(无习题)

hope kc 2024-10-24 17:35:02 阅读 66

#1024程序员节|征文#

在这里插入图片描述

C++ 中的 <code>stack 和 queue 容器详细总结

1. 概述

C++ 标准模板库(STL)提供了一系列容器,其中 stackqueue 是两种常用的适配器容器。它们基于底层的序列容器(如 vectordeque)实现,分别用于支持栈和队列的操作模型。栈(stack)遵循“后进先出”(LIFO)的原则,而队列(queue)遵循“先进先出”(FIFO)的原则。本文将详细介绍这两种容器的特点、使用方法、实现机制及其应用场景。

2. stack 容器

2.1 什么是 stack

stack 是一种基于“后进先出”(LIFO, Last In First Out)数据结构的容器。它的特点是只能在栈的顶部进行元素的插入和删除操作。栈的操作类似于现实中的叠盘子,最新添加的盘子在最上面,也最先被移除。

2.2 stack 的特点

后进先出(LIFO):只能访问栈顶的元素,插入和删除也都只能在栈顶进行。限制性操作stack 只允许在栈顶插入(push)和删除(pop)元素,无法随机访问中间的元素。适配器容器stack 并非独立的容器,而是基于其他序列容器(如 dequevectorlist)实现的适配器。

2.3 stack 的常用操作

push(value):将元素压入栈顶。

#include <stack>

#include <iostream>

int main() {

std::stack<int> s;

s.push(10); // 将 10 压入栈顶

s.push(20); // 将 20 压入栈顶

return 0;

}

pop():移除栈顶元素。

s.pop(); // 移除栈顶元素 20

top():访问栈顶元素。

int topElement = s.top(); // 获取栈顶元素 10

empty():判断栈是否为空。

if (s.empty()) {

std::cout << "栈为空" << std::endl;

}

size():返回栈中的元素个数。

std::cout << "栈的大小: " << s.size() << std::endl;

2.4 stack 的底层实现

stack 是一个容器适配器,底层通常使用 deque 作为默认的序列容器,也可以使用 vectorlist。由于 stack 只暴露了栈的接口,底层的容器类型对外不可见。

以下代码展示了如何使用 deque 来实现一个 stack

std::stack<int, std::deque<int>> s; // 使用 deque 作为底层容器

stack 的默认实现是基于 deque,因为 deque 支持高效的头部和尾部插入与删除操作。而 vector 虽然也可以用来实现 stack,但在需要扩展时可能需要重新分配内存,性能可能不如 deque 稳定。

2.5 stack 的应用场景

函数调用管理:在编译器的实现中,stack 常被用来管理函数调用,包括参数传递、返回地址保存等。表达式求值:在中缀表达式到后缀表达式的转换、后缀表达式的计算中,stack 都能起到重要的作用。括号匹配stack 常用于判断括号是否匹配的问题,可以高效地检查表达式中的括号是否正确闭合。回溯问题stack 也常用于深度优先搜索(DFS)等需要回溯的算法中。

2.6 stack 的优缺点

优点

操作简单,只需关注栈顶的元素。插入和删除操作的时间复杂度为 O(1)。 缺点

无法随机访问元素,限制了灵活性。只能通过 pushpop 操作进行数据操作。

3. queue 容器

3.1 什么是 queue

queue 是一种基于“先进先出”(FIFO, First In First Out)数据结构的容器。它的特点是只能在队尾插入元素,在队头删除元素,类似于排队的过程,最先进入队列的元素最先被移除。

3.2 queue 的特点

先进先出(FIFO):元素只能从队尾插入,从队头移除。适配器容器queuestack 类似,也是基于其他序列容器(如 dequelist)实现的适配器。限制性操作queue 只允许在队尾插入和在队头移除,无法随机访问中间的元素。

3.3 queue 的常用操作

push(value):将元素添加到队尾。

#include <queue>

#include <iostream>

int main() {

std::queue<int> q;

q.push(10); // 将 10 添加到队尾

q.push(20); // 将 20 添加到队尾

return 0;

}

pop():移除队头元素。

q.pop(); // 移除队头元素 10

front():访问队头元素。

int frontElement = q.front(); // 获取队头元素 20

back():访问队尾元素。

int backElement = q.back(); // 获取队尾元素 20

empty():判断队列是否为空。

if (q.empty()) {

std::cout << "队列为空" << std::endl;

}

size():返回队列中的元素个数。

std::cout << "队列的大小: " << q.size() << std::endl;

3.4 queue 的底层实现

queue 通常使用 deque 作为默认的底层容器,也可以使用 listdeque 是双端队列,支持高效的头尾操作,因此非常适合用于实现 queue

以下代码展示了如何使用 list 作为底层容器实现一个 queue

std::queue<int, std::list<int>> q; // 使用 list 作为底层容器

3.5 queue 的应用场景

任务调度queue 常用于任务调度中,例如打印任务、进程任务等,确保任务按照先后顺序执行。广度优先搜索(BFS):在图算法中,queue 常用于实现广度优先搜索,逐层访问节点。缓冲区queue 常用于实现缓冲区,保证数据的顺序处理,例如在多线程编程中用于生产者-消费者模型。

3.6 queue 的优缺点

优点

结构简单,保证元素按顺序被处理。插入和删除操作的时间复杂度为 O(1)。 缺点

无法随机访问元素。只支持从队尾插入和从队头移除,限制了灵活性。

4. priority_queue 容器

4.1 什么是 priority_queue

priority_queue 是一种特殊的队列,其元素根据优先级进行排序。默认情况下,priority_queue 中的元素是按大顶堆(最大元素优先)进行排序的,即优先级最高的元素最先出队。

4.2 priority_queue 的特点

优先级排序:元素按优先级进行排序,最大或最小的元素优先出队。基于堆实现priority_queue 通常使用堆结构(如二叉堆)来实现,以保证元素的插入和删除操作具有对数级的时间复杂度。只允许访问队头元素:与普通 queue 类似,priority_queue 只允许访问和移除队头元素,不能随机访问其他元素。

4.3 priority_queue 的常用操作

push(value):将元素添加到队列中,并根据优先级调整位置。

#include <queue>

#include <iostream>

int main() {

std::priority_queue<int> pq;

pq.push(30); // 将 30 添加到队列中

pq.push(10); // 将 10 添加到队列中

pq.push(20); // 将 20 添加到队列中

return 0;

}

pop():移除优先级最高的元素。

pq.pop(); // 移除队头元素 30(最大值)

top():访问优先级最高的元素。

int topElement = pq.top(); // 获取优先级最高的元素 20

empty():判断队列是否为空。

if (pq.empty()) {

std::cout << "优先队列为空" << std::endl;

}

size():返回队列中的元素个数。

std::cout << "优先队列的大小: " << pq.size() << std::endl;

4.4 priority_queue 的底层实现

priority_queue 使用堆来实现,默认情况下是大顶堆(最大值优先),可以通过指定比较器来实现小顶堆。

以下代码展示了如何使用小顶堆来实现一个 priority_queue

std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap; // 小顶堆

template<class T>

class less

{

public:

bool operator()(const T& x, const T& y)

{

return x < y;

}

}; //仿函数,判断大小

template<class T>

class greater

{

public:

bool operator()(const T& x, const T& y)

{

return x > y;

}

}; //仿函数,判断大小

————————————————

如果元素为自定义类型,则需要用户自己重载仿函数

4.5 priority_queue 的应用场景

任务调度:在操作系统中,priority_queue 可以用于实现优先级调度,让优先级高的任务先执行。最短路径算法:在图算法中,priority_queue 常用于 Dijkstra 算法,以找到权重最小的路径。事件驱动模拟:在事件驱动的模拟系统中,priority_queue 可以根据事件的优先级处理事件,确保最重要的事件最先被处理。

4.6 priority_queue 的优缺点

优点

能够根据优先级高效地管理元素。插入和删除的时间复杂度为 O(log n),适合处理大量需要排序的元素。 缺点

无法随机访问元素。只允许访问优先级最高的元素,限制了灵活性。

5. stackqueuepriority_queue 的对比

5.1 访问方式

stack:后进先出,只能访问栈顶元素。queue:先进先出,只能访问队头和队尾元素。priority_queue:按照优先级排序,只能访问优先级最高的元素。

5.2 应用场景

stack:适用于需要后进先出逻辑的场景,例如递归、函数调用管理等。queue:适用于需要先进先出逻辑的场景,例如任务调度、广度优先搜索等。priority_queue:适用于需要根据优先级处理元素的场景,例如任务调度、图算法等。

5.3 底层实现

stack:基于 deque(默认)、vectorlistqueue:基于 deque(默认)或 listpriority_queue:基于堆,通常使用 vector 实现。

5.4 时间复杂度

stack:插入和删除操作的时间复杂度为 O(1)。queue:插入和删除操作的时间复杂度为 O(1)。priority_queue:插入和删除操作的时间复杂度为 O(log n)。

6. 示例代码总结

以下是一个完整的示例代码,展示了 stackqueuepriority_queue 的基本使用方法:

#include <iostream>

#include <stack>

#include <queue>

int main() {

// stack 示例

std::stack<int> s;

s.push(1);

s.push(2);

s.push(3);

std::cout << "Stack top: " << s.top() << std::endl;

s.pop();

std::cout << "Stack top after pop: " << s.top() << std::endl;

// queue 示例

std::queue<int> q;

q.push(1);

q.push(2);

q.push(3);

std::cout << "Queue front: " << q.front() << ", back: " << q.back() << std::endl;

q.pop();

std::cout << "Queue front after pop: " << q.front() << std::endl;

// priority_queue 示例

std::priority_queue<int> pq;

pq.push(10);

pq.push(5);

pq.push(20);

std::cout << "Priority queue top: " << pq.top() << std::endl;

pq.pop();

std::cout << "Priority queue top after pop: " << pq.top() << std::endl;

return 0;

}

7. 总结

C++ 中的 stackqueuepriority_queue 容器为开发者提供了实现特定数据结构的便捷方式。stack 采用后进先出的逻辑,适合处理递归和回溯问题;queue 采用先进先出的逻辑,适合任务调度和广度优先搜索;priority_queue 根据优先级处理元素,适合任务调度和图算法。在使用这些容器时,需要根据具体的应用场景选择合适的容器,以便最大化性能和代码简洁性。每种容器都有其特定的用途和优势,合理选择可以有效简化程序设计,并提升程序的可维护性和效率。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。