文章目录

    • 1. Theoretical basis
      • The C++ standard library has multiple versions. To understand the implementation principles of `stack` and `queue`, we must know which STL version we are using.
      • The `stack` and `queue` discussed next are data structures in *SGI STL*. Only by knowing the version can we understand the underlying implementation.
    • 2. Question check-in
        • 【232. Implement Queue using Stacks】
        • 【225. Implement Stack using Queues】
        • 【20. Valid Parentheses】
        • 【1047. Remove All Adjacent Duplicates In String】

1. Theoretical basis

A queue is first-in-first-out, while a stack is first-in-last-out.

Consider four questions:

  1. Is stack in C++ a container?
  2. Which version of STL does the stack we use belong to?
  3. How is stack implemented in the STL we use?
  4. Does stack provide iterators to traverse its space?

The C++ standard library has multiple versions. To understand the implementation principles of stack and queue, we must know which STL version we are using.

Let’s introduce the three most common STL versions:

  1. HP STL: Other versions of C++ STL are generally based on HP STL, which is the first implementation of C++ STL and is open-source.

  2. P.J.Plauger STL: Implemented by P.J.Plauger based on HP STL, adopted by the Visual C++ compiler, and is not open-source.

  3. SGI STL: Implemented by Silicon Graphics Computer Systems based on HP STL, adopted by the GCC compiler in Linux. SGI STL is open-source, and its code is highly readable.

The stack and queue discussed next are data structures in SGI STL. Only by knowing the version can we understand the underlying implementation.

Now, let’s talk about the stack—last-in-first-out, as shown in the diagram:
在这里插入图片描述
A stack provides interfaces such as push and pop. All elements must follow the Last-In-First-Out (LIFO) rule, so a stack does not offer traversal functionality or iterators. Unlike sets or maps, which provide iterators to traverse all elements.
The stack relies on an underlying container to perform all its operations, offering a unified interface externally. The underlying container is pluggable (meaning we can choose which container to use to implement the stack’s functionality).
Therefore, in the STL, a stack is often not classified as a container but as a container adapter.
So the question arises: What container is used to implement a stack in the STL?
The internal structure of a stack can use vector, deque, or list as its underlying implementation—essentially either an array or a linked list.
In the commonly used SGI STL, if no underlying implementation is specified, the default underlying structure for a stack is deque.
A deque is a double-ended queue. By sealing one end and leaving the other open, the logic of a stack can be achieved.
In the SGI STL, the default underlying implementation for a queue also uses deque.
We can also specify a vector as the underlying implementation for a stack, with the initialization statement as follows:
std::stack<int, std::vector<int>> third; // A stack using vector as the underlying container
As mentioned earlier regarding the characteristics of a stack, the same applies to queues.
A queue follows the First-In-First-Out (FIFO) data structure and similarly does not allow traversal or provide iterators. In the SGI STL, the default underlying structure for a queue is also deque.
Alternatively, a list can be specified as its underlying implementation, with the initialization statement for a queue as follows:
std::queue<int, std::list<int>> third; // Defines a queue using list as the underlying container
Therefore, in the STL, a queue is also not classified as a container but as a container adapter.

2. Question check-in

【232. Implement Queue using Stacks】

Problem Link
Approach: Use stacks to simulate queue behavior. A single stack alone won’t suffice, so two stacks are needed—an input stack and an output stack. Pay attention to the relationship between them. Refer to the animation below for the approach:

Animation for Implementing Queue with Stacks

  1. When pushing data, simply place it into the input stack.
  2. For popping, the operation is more complex. If the output stack is empty, transfer all data from the input stack (note: transfer all), then pop from the output stack. If the output stack is not empty, simply pop directly from it.
  3. Finally, how to determine if the queue is empty? If both the input and output stacks are empty, the simulated queue is empty.
class MyQueue {
public:stack<int> stIn;stack<int> stOut;/** Initialize your data structure here. */MyQueue() {}/** Push element x to the back of queue. */void push(int x) {stIn.push(x);}/** Removes the element from in front of queue and returns that element. */int pop() {// Only when stOut is empty, transfer data from stIn (transfer all data from stIn)if (stOut.empty()) {// Transfer data from stIn until stIn is emptywhile(!stIn.empty()) {stOut.push(stIn.top());stIn.pop();}}int result = stOut.top();stOut.pop();return result;}/** Get the front element. */int peek() {int res = this->pop(); // Reuse existing pop functionstOut.push(res); // Since pop removed the element res, push it backreturn res;}/** Returns whether the queue is empty. */bool empty() {return stIn.empty() && stOut.empty();}
};

Senior’s Insight:
Notice how peek() reuses pop(). Otherwise, the logic for checking if stOut is empty would have to be rewritten.

A bit more on coding best practices: In industrial-level code development, the worst habit is copying and pasting similar functions with minor tweaks.

This leads to messy project code. Always prioritize reusability. Abstract similar functions instead of copying and pasting extensively—it’s prone to errors! (Those who’ve stumbled know this well.)

【225. Implement Stack using Queues】

Problem Link
Approach: Still, two queues are used to simulate a stack, but instead of an input-output relationship, the second queue serves purely as a backup!

Two queues, que1 and que2, are employed to implement the stack functionality. que2 acts entirely as a backup—storing all elements of que1 except the last one, then popping the last element, and finally restoring the remaining elements from que2 back to que1.

class MyStack {
public:queue<int> que1;queue<int> que2; // Auxiliary queue for backup  /** Initialize your data structure here. */MyStack() {}/** Push element x onto stack. */void push(int x) {que1.push(x);}/** Removes the element on top of the stack and returns that element. */int pop() {int size = que1.size();size--;while (size--) { // Transfer que1 to que2, leaving the last element  que2.push(que1.front());que1.pop();}int result = que1.front(); // The remaining element is the one to return  que1.pop();que1 = que2;            // Assign que2 back to que1  while (!que2.empty()) { // Clear que2  que2.pop();}return result;}/** Get the top element.  ** Cannot use back() directly.  */int top() {int size = que1.size();size--;while (size--) {// Transfer que1 to que2, leaving the last element  que2.push(que1.front());que1.pop();}int result = que1.front(); // The remaining element is the one to return  que2.push(que1.front());   // After retrieving the value, add the last element to que2 to maintain the original structure  que1.pop();que1 = que2; // Assign que2 back to que1  while (!que2.empty()) {// Clear que2  que2.pop();}return result;}/** Returns whether the stack is empty. */bool empty() {return que1.empty();}
};

Optimized Solution:
Actually, this problem can be solved with just one queue.

When simulating stack pop operations with a single queue, simply re-add the elements at the front of the queue (except the last one) to the rear of the queue. Then, the popping order will match the stack’s order.

class MyStack {
public:queue<int> que;MyStack() {}void push(int x) {que.push(x);}int pop() {int size = que.size();size--;while (size--) { // Re-add elements at the front (except the last one) to the rear  que.push(que.front());que.pop();}int result = que.front(); // Now, the popping order matches the stack's order  que.pop();return result;}int top() {int size = que.size();size--;while (size--) {// Re-add elements at the front (except the last one) to the rear  que.push(que.front());que.pop();}int result = que.front(); // The obtained element is the top of the stack  que.push(que.front());    // Re-add the retrieved element to the rear to preserve the data structure  que.pop();return result;}bool empty() {return que.empty();}
};
【20. Valid Parentheses】

Problem Link
Approach: Using a stack to match left and right parentheses is straightforward.
An optimization is to push the corresponding right parenthesis when encountering a left parenthesis, simplifying the code by only comparing the current element with the top of the stack—much easier than pushing left parentheses first!

class Solution {
public:bool isValid(string s) {if (s.size() % 2 != 0) return false; // Odd length, definitely invalidstack<char> st;for (int i = 0; i < s.size(); i++) {if (s[i] == '(') st.push(')');else if (s[i] == '{') st.push('}');else if (s[i] == '[') st.push(']');// Case 3: Stack is empty during traversal, meaning no matching left parenthesis for a right parenthesis  // Case 2: Mismatched character found during traversal  else if (st.empty() || st.top() != s[i]) return false;else st.pop(); // st.top() matches s[i], pop the stack  }// Case 1: Traversal complete but stack isn't empty, meaning unmatched left parentheses remain  return st.empty();}
};
【1047. Remove All Adjacent Duplicates In String】

Problem Link
Approach: Use a stack to process data while pushing:
Push if no duplicate or stack is empty; otherwise, pop.

class Solution {
public:string removeDuplicates(string S) {stack<char> st;for (char s : S) {if (st.empty() || s != st.top()) {st.push(s);} else {st.pop(); // s matches st.top()  }}string result = "";while (!st.empty()) { // Collect stack elements into result  result += st.top();st.pop();}reverse(result.begin(), result.end()); // Reverse the string  return result;}
};

Alternatively, use the string directly as a stack to avoid converting the stack to a string:

class Solution {
public:string removeDuplicates(string S) {string result;for(char s : S) {if(result.empty() || result.back() != s) {result.push_back(s);}else {result.pop_back();}}return result;}
};

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql数据仓库备份脚本

Mysql数据仓库备份脚本 #!/bin/bash# MySQL数据库完整备份脚本 # 功能: 查询所有数据库 -> 分别导出 -> 压缩打包# 配置区域 # MySQL连接信息 MYSQL_USER"root" MYSQL_PASSWORD"root" MYSQL_HOST"localhost" MYSQL_PORT"3306"…

基于嵌入式Linux RK3568 qt 车机系统开发

嵌入式系统、Qt/QML 与车机系统的发展趋势分析 1. RK3568 开发板与 OpenGL ES 3 支持&#xff0c;为图形应用打下坚实基础 RK3568 是瑞芯微&#xff08;Rockchip&#xff09;推出的一款高性能、低功耗的64位处理器&#xff0c;广泛用于工业控制、智能终端、嵌入式车载系统等领…

OceanBase架构设计

本文主要参考《大规模分布式存储系统》 基本结构客户端&#xff1a;发起请求。 RootServer&#xff1a;管理集群中的所有服务器&#xff0c;子表数据分布及副本管理&#xff0c;一般为一主一备&#xff0c;数据强同步。 UpdateServer&#xff1a;存储增量变更数据&#xff0c;一…

[Element-plus]动态设置组件的语言

nuxt element-plus国际化vue element-plus国际化<template><div class"container"> <!-- <LangSwitcher />--><button click"toggle(zh-cn)">中文</button><button click"toggle(en)">English<…

【VS Code - Qt】如何基于Docker Linux配置Windows10下的VS Code,开发调试ARM 版的Qt应用程序?

如何在Windows 10上配置VS Code以开发和调试ARM版Qt应用程序。这需要设置一个基于Docker的Linux环境。首先&#xff0c;让我们了解一下你的具体需求和环境&#xff1a;你有一个Qt项目&#xff08;看起来是医学设备相关的设置程序&#xff09;目标平台是ARM架构你希望在Windows …

linux常见故障系列文章 1-linux进程挂掉原因总结和排查思路

问题一 &#xff1a;运行时常见的进程崩溃原因 内存不足&#xff09; **0. 内存不足 内存不足&#xff08;OOM Killer&#xff09; 排查 OOM&#xff1a;free -h → dmesg → ps aux --sort-%mem 预防 OOM&#xff1a;限制关键进程内存、调整 OOM Killer 策略、增加 swap 长期优…

Spring Cloud Gateway 路由与过滤器实战:转发请求并添加自定义请求头(最新版本)

前言 网关是什么?如果把你的系统比作一栋高端写字楼,网关就是那位神通广大的前台小姐姐,笑容可掬地拦住不速之客,把贵宾引到豪华会议室,还会在你胸口贴上一枚醒目的“贵宾”标签。它既懂礼数,又有原则,能过滤无效请求、转发正确目标,还能在途中动点“小手脚”,比如加…

达梦数据库慢SQL日志收集和分析

达梦数据库慢SQL日志收集和分析 开启SQL日志记录 使用DMLOG工具分析SQLLOG DMLOG安装配置 DMLOG分析日志 系统视图V$LONG_EXEC_SQLS记录了最近1000条执行时间超1s的sql。如果sql语句超长可能会被截断,只能从sqllog里找完整的sql文本。 SELECT * FROM V$LONG_EXEC_SQLS ORDER …

一篇文章,带你玩转SparkCore

Spark Core 概念 前言 批处理&#xff08;有界数据&#xff09; ​ 对静态的、有限的数据集进行一次性处理&#xff0c;数据通常按固定周期&#xff08;如每小时、每天&#xff09;收集后统一计算。 特点&#xff1a; 高吞吐量&#xff0c;适合大规模数据。高延迟&#xff08;数…

VRRP技术

VRRP的概念及应用场景 VRRP&#xff08;虚拟路由冗余协议&#xff09;概念 VRRP&#xff08;Virtual Router Redundancy Protocol&#xff0c;虚拟路由冗余协议&#xff09;是一种路由容错协议&#xff0c;用于在多个路由器之间提供网关冗余&#xff0c;确保当主路由器故障时&a…

表驱动法-灵活编程范式

表驱动法&#xff1a;从理论到实践的灵活编程范式 一、为什么需要表驱动法&#xff1f; 在处理多分支逻辑&#xff08;如消息解析、命令分发&#xff09;时&#xff0c;传统的 if-else 或 switch-case 存在明显局限&#xff1a; 当分支数量庞大&#xff08;如成百上千条命令&am…

零基础-动手学深度学习-10.2. 注意力汇聚:Nadaraya-Watson 核回归

上节介绍了框架下的注意力机制的主要成分 图10.1.3&#xff1a; 查询&#xff08;自主提示&#xff09;和键&#xff08;非自主提示&#xff09;之间的交互形成了注意力汇聚&#xff1b; 注意力汇聚有选择地聚合了值&#xff08;感官输入&#xff09;以生成最终的输出。 本节将…

nginx高新能web服务器

一、Nginx 概述和安装 Nginx是免费的、开源的、高性能的HTTP和反向代理服务器、邮件代理服务器、以及TCP/UDP代理服务器。 Nginx 功能介绍 静态的web资源服务器html&#xff0c;图片&#xff0c;js&#xff0c;css&#xff0c;txt等静态资源 http/https协议的反向代理 结合F…

Unity大型场景性能优化全攻略:PC与安卓端深度实践 - 场景管理、渲染优化、资源调度 C#

本文将深入探讨Unity在大型场景中的性能优化策略&#xff0c;涵盖场景管理、渲染优化、资源调度等核心内容&#xff0c;并提供针对PC和安卓平台的优化方案及实战案例。 提示&#xff1a;内容纯个人编写&#xff0c;欢迎评论点赞。 文章目录1. 大型场景性能挑战1.1 性能瓶颈定位…

Java集合框架、Collection体系的单列集合

Java集合框架、Collection1. 认识Java集合框架及结构1.1 集合框架整体结构1.2 集合框架的核心作用2. Collection的两大常用集合体系及各个系列集合的特点2.1 List系列集合&#xff08;有序、可重复&#xff09;2.2 Set系列集合&#xff08;无序、不可重复&#xff09;3. Collec…

HTML <picture> 元素:让图片根据设备 “智能切换” 的响应式方案

在响应式设计中&#xff0c;图片适配是一个绕不开的难题&#xff1a;同一张高清图片在大屏设备上清晰美观&#xff0c;但在小屏手机上可能加载缓慢&#xff1b;而适合手机的小图在桌面端又会模糊失真。传统的解决方案往往需要用JavaScript判断设备尺寸并动态替换图片源&#xf…

Spring Boot 监控与日志管理实战

在 Spring Boot 应用开发中&#xff0c;指标监控和日志管理是保障应用稳定运行的核心环节。指标监控能实时掌握应用健康状态、性能瓶颈&#xff0c;日志管理则用于问题排查和安全审计。本文基于 Spring Boot 提供的 Actuator 监控工具、Spring Boot Admin 可视化平台&#xff0…

【排序算法】②希尔排序

系列文章目录 第一篇&#xff1a;【排序算法】①直接插入排序-CSDN博客 第二篇&#xff1a;【排序算法】②希尔排序-CSDN博客 第三篇&#xff1a;【排序算法】③直接选择排序-CSDN博客 第四篇&#xff1a;【排序算法】④堆排序-CSDN博客 第五篇&#xff1a;【排序算法】⑤冒…

Linux Shell为文件添加BOM并自动转换为unix格式

1.添加并查看BOM添加bomvim -c "set bomb|set fileencodingutf-8|wq" ./gradlew查看bomhead -c 3 ./gradlew | hexdump -C2.安装dos2unix并转换为unix格式安装sudo apt install dos2unix转换dos2unix ./gradlew

华清远见25072班C语言学习day5

重点内容&#xff1a;数组&#xff1a;为什么有数组&#xff1f;为了便于存储多个数据特点&#xff1a;连续存储多个同种数据类型元素(连续指内存地址连续)数组名&#xff1a;数组中首元素的地址&#xff0c;是一个地址常量。一维整形数组&#xff1a;定义&#xff1a;数据类型…