栈 & 队列:面试题(括号 / 循环队列)+ 概念题,全考点覆盖

/-----------初阶数据结构-----------/

【 时间复杂度+空间复杂度 】

【 顺序表 】

【 单链表 】

【 链表OJ题(上篇)】

【 链表OJ题(下篇)】


【栈和队列面试题目录】:

[一、 栈和队列面试题](#一、 栈和队列面试题)

Ⅰ、有效的括号

Ⅱ、用队列实现栈

Ⅲ、用栈实现队列

Ⅳ、设计循环队列

二、概念选择题


谁学数据结构没被栈和队列 "折磨" 过?面试要写括号匹配、用栈造队列,选择题还得算循环队列长度、辨出栈序列 ------ 既要敲代码又要抠概念,主打一个 "全方位考察"。这篇直接端上 4 道面试题 + 5 道选择题,解法 + 坑点 + 考点一次性捋顺,主打一个 "练完少踩坑"。

一、 栈和队列面试题

Ⅰ、有效的括号

【题目】:https://leetcode.cn/problems/valid-parentheses/

【思路】:

用栈存左括号,遇到右括号就弹出栈顶匹配,不匹配 / 栈空 / 最后栈非空都返回 false,反之合法

【代码】:

cpp 复制代码
class Solution {
public:
    bool isValid(string s) {
         stack<char> st;
         int i = 0;
         while(i < s.size())
         {
            // 左括号:入栈暂存,等待匹配
            if(s[i] == '[' || s[i] == '(' || s[i] == '{')
            {
                st.push(s[i]);
            }
            // 右括号:校验匹配性
            else
            {
                // 边界1:只有右括号(栈空无左括号可匹配)
                if(st.empty())
                {
                    return false;
                }
                  
                char top = st.top();
                st.pop();
                 
                // 核心校验:栈顶左括号与当前右括号类型不匹配
                if(top == '(' && s[i] != ')'||top == '[' && s[i] != ']'||top == '{' && s[i] != '}')
                {
                    return false;
                }
            }
            i++;
         }
         // 边界2:左括号未匹配完(栈非空)
         int ret = st.empty();
         return ret;
    }
};

Ⅱ、用队列实现栈

【题目】:https://leetcode.cn/problems/implement-stack-using-queues/

【思路】:

【代码】:

cpp 复制代码
class MyStack {
public:
    MyStack() 
    {

    }
    
    // 核心:新元素始终压入非空队列,保证数据集中在一个队列里(模拟栈的元素存储)
    void push(int x) {
         if(!q1.empty())
         {
             q1.push(x);
         }
         else
         {
              q2.push(x);
         }
    }
    
    // 核心:把非空队列的前n-1个元素移到空队列,仅留最后1个(栈顶元素)弹出,模拟栈"后进先出"
    int pop() {
        // 处理q1非空的情况
         if(!q1.empty())
         {
             // 移走q1中除最后1个外的所有元素到q2
             while(q1.size()>1)
             {
                q2.push(q1.front());
                q1.pop();
             }
               // 取出栈顶元素(q1仅剩的最后一个元素)
               int top = q1.front();
               q1.pop();
               return top;
         }
         // 处理q2非空的情况(逻辑和q1完全一致)
         else
         {
            while(q2.size()>1)
             {
                q1.push(q2.front());
                q2.pop();
             }
              int top = q2.front();
              q2.pop();
              return top;
         }
    }
    
    // 核心:栈顶元素 = 非空队列的队尾元素(队列内元素顺序和栈一致)
    int top() {
         if(!q1.empty())
         {
             return q1.back();
         }
         else
         {
             return q2.back();
         }
    }
    
    // 核心:栈空 = 两个队列都为空(无任何元素残留)
    bool empty() {
        return q1.empty() && q2.empty();
    }

private:
    queue<int> q1; // 辅助队列1
    queue<int> q2; // 辅助队列2
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

Ⅲ、用栈实现队列

【题目】:https://leetcode.cn/problems/implement-queue-using-stacks/

【思路】:

【代码】:

cpp 复制代码
class MyQueue {
public:
    MyQueue() {}
    
    // 核心:入队只往pushst压入元素
    void push(int x) {
         pushst.push(x);
    }
    
    // 核心:popst空则将pushst元素倒过来(反转顺序),取popst栈顶(队头)弹出
    int pop() {
        if(popst.empty()) {
            // pushst元素倒入popst,实现先进先出
            while(!pushst.empty()) {
                  popst.push(pushst.top());
                  pushst.pop();
            }
        }
  
        assert(!popst.empty()); // 防御:避免队空时调用pop
        int ret = popst.top();
        popst.pop();
        return ret;
    }
    
    // 核心:逻辑同pop,仅取队头不弹出
    int peek() {
        if(popst.empty()) {
            while(!pushst.empty()) {
                  popst.push(pushst.top());
                  pushst.pop();
            }
        }
        assert(!popst.empty()); // 防御:避免队空时调用peek
        return popst.top();
    }
    
    // 核心:队列空 = 两个栈都为空
    bool empty() {
         return popst.empty() && pushst.empty();
    }

private:
    stack<int> pushst; // 仅用于入队的栈
    stack<int> popst;  // 仅用于出队/查队头的栈
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */

Ⅳ、设计循环队列

【题目】:https://leetcode.cn/problems/design-circular-queue/

【思路】:

这道题的核心是实现循环队列的完整操作接口,如果你对循环队列的实现细节不太熟悉,可以参考我之前的内容,里面有循环队列的详细实现方案,能帮你快速理清思路~【参考文章

【代码】:

循环队列的实现方案较为灵活,本次我采用数组的方式来完成具体实现~

cpp 复制代码
class MyCircularQueue {
public:
    // 构造函数:初始化循环队列,分配k大小的数组空间,初始化指针/计数变量
    MyCircularQueue(int k) {
        a = new int[k];          // 堆区分配数组,容量为k
        front = rear = size = 0; // 队头/队尾指针、元素个数初始化为0
        capacity = k;            // 队列最大容量设为k
    }

    // 核心:判空 - 元素个数为0则队空
    bool isEmpty() {
        return size == 0;
    }
    
    // 核心:判满 - 元素个数等于容量则队满
    bool isFull() {
        return size == capacity;
    }
        
    // 核心:入队 - 队不满时,将值存入rear位置,rear循环后移,元素个数+1
    bool enQueue(int value) 
    {
         if(isFull())            // 队满则入队失败
            return false;

         a[rear] = value;        // 新元素存入当前rear位置
         rear = (rear + 1) % capacity; // rear循环后移(取模实现循环)
         size++;                 // 元素个数+1

         return true;            // 入队成功
    }
    
    // 核心:出队 - 队不空时,front循环后移,元素个数-1
    bool deQueue() 
    {
        if(isEmpty())            // 队空则出队失败
           return false;
     
        front = (front + 1) % capacity; // front循环后移(取模实现循环)
        size--;                  // 元素个数-1
        return true;             // 出队成功
    }
    
    // 核心:取队头 - 队不空时返回front指向的元素
    int Front() {
          if(isEmpty())
           return -1;            // 队空返回-1(题目约定)
        return a[front];
    }
    
    // 核心:取队尾 - 队不空时,根据rear位置分支取队尾元素
    int Rear() {
          if(isEmpty())
           return -1;            // 队空返回-1(题目约定)
          // rear指向队尾下一个位置:
          // 1. rear=0 → 队尾在数组最后一位(capacity-1)
          // 2. rear≠0 → 队尾在rear前一位(rear-1)
          return rear == 0 ? a[capacity-1] : a[rear-1];
    }
    
private:
    int *a;          // 数组:存储循环队列元素
    int front;       // 队头指针:指向队头元素
    int rear;        // 队尾指针:指向队尾元素的下一个位置
    int size;        // 当前元素个数:快速判空/判满
    int capacity;    // 队列最大容量:数组总大小
};

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue* obj = new MyCircularQueue(k);
 * bool param_1 = obj->enQueue(value);
 * bool param_2 = obj->deQueue();
 * int param_3 = obj->Front();
 * int param_4 = obj->Rear();
 * bool param_5 = obj->isEmpty();
 * bool param_6 = obj->isFull();
 */

二、概念选择题

栈是 "后进先出"------ 最后入栈的元素最先出栈。入栈顺序是1、2、3、4、5、A、B、C、D、E(E 最后入栈),出栈就从 E 开始倒着取,顺序是E、D、C、B、A、5、4、3、2、1,对应选项B


逐一模拟验证选项:

  • A:1 入→1 出;2、3、4 入→4 出、3 出、2 出 → 可行;
  • B:1、2 入→2 出;3 入→3 出;4 入→4 出;1 出 → 可行;
  • C :要先出 3,需 1、2、3 入→3 出;此时栈内是 1、2(2 在栈顶),但选项接下来出 1------1 在 2 下面,无法跳过 2 先出 1 → 不可行;
  • D:1、2、3 入→3 出;4 入→4 出;2 出、1 出 → 可行。

循环队列中,front=rear对应两种情况:

  1. 队空:元素数 = 0;
  2. 队满:元素数 = 容量(本题为 100)。

结论:元素数为 0 或 100,选 D。


队列是 "先进先出" 结构,基本运算只针对队头 / 队尾操作:

  • A(队尾插入)、C(判空)、D(读取队头)都是队列基本运算;
  • B(删除第 i 个元素)是 "随机访问删除",违背队列 "先进先出" 的操作规则,不是队列基本运算。

步骤 1:画 "未绕圈" 场景

设 N=5,front=0(空),rear=4(有效元素是 1、2、3、4,共 4 个)。

  • 代入选项 B:(4 - 0 + 5) % 5 = 9 % 5 = 4(= 实际元素数 4,对)

步骤 2:画 "绕圈" 场景

设 N=5,front=2(空),rear=1(有效元素是 2、1、3、4,共 4 个)。

  • 代入选项 B:(1 - 2 + 5) % 5 = 4 % 5 = 4(= 实际元素数 4,对)
相关推荐
EXtreme353 小时前
算法深潜:链表中的生死之环(LeetCode 141 & 142 详解)
数据结构·算法·leetcode·链表·快慢指针·数学证明·带环链表
seven97_top3 小时前
数据结构——树
java·数据结构
leoufung3 小时前
LeetCode 211:设计添加与搜索单词的数据结构(Trie + DFS)
数据结构·leetcode·深度优先
FuckPatience3 小时前
C# BinarySearch 的返回值
开发语言·数据结构·c#
代码雕刻家3 小时前
1.8.课设实验-数据结构-哈夫曼树的建立与应用
c语言·数据结构
无限进步_4 小时前
寻找数组中缺失数字:多种算法详解与比较
c语言·开发语言·数据结构·算法·排序算法·visual studio
xu_yule4 小时前
数据结构(4)链表概念+单链表实现
数据结构·算法·链表
此生只爱蛋4 小时前
【Redis】浅谈数据结构和内部编码和单线程架构
数据结构·数据库·redis
山峰哥4 小时前
现代 C++ 的炼金术:铸就高性能与高可维护性的工程实践
java·开发语言·前端·数据结构·c++