判断题:栈的特点是后进先出(LIFO)。
考点:后进先出(A1)。
解析:栈(stack)只在一端(栈顶)操作:后进先出(LIFO)——最后压入的元素最先弹出,像一摞盘子。✅ 正确
排除法:无(判断题)。混淆点:队列才是先进先出(FIFO,见 C1)——"栈后进先出、队列先进先出"是数据结构第一对概念。
关联 · 栈顶栈底(A2):所有操作集中在栈顶——LIFO 由"单端操作"保证。
判断题:栈的插入和删除都发生在栈顶这一端,栈底固定不动。
考点:栈顶与栈底(A2)。
解析:栈顶是唯一能插入/删除的一端;栈底是最先入栈的元素所在,固定不动。✅ 正确
排除法:无(判断题)。混淆点:栈底元素要等它上面的全部弹出才能出来——"先进去的最后出来"。
关联 · 入栈出栈(A3):
push压入栈顶、pop弹出栈顶——操作永远发生在栈顶。
判断题:入栈(push)把元素放到栈顶,出栈(pop)取出栈顶元素。
考点:入栈出栈(A3)。
解析:入栈 push(x):x 成为新栈顶;出栈 pop():取出并移除当前栈顶。✅ 正确
排除法:无(判断题)。混淆点:pop 会删除元素,top 只看不删(A4)——一对易混操作。
关联 · 取栈顶(A4):
top/peek查看栈顶值但不删除——"看"与"取"是两种操作。
判断题:top 操作只查看栈顶元素的值,不删除它。
考点:取栈顶(A4)。
解析:top(或 peek)只返回栈顶元素的值,不修改栈——栈顶元素还在。✅ 正确
排除法:无(判断题)。混淆点:pop 取出并删除;top 只读——判空、比较栈顶常用 top。
关联 · 括号匹配(B1):匹配括号时用
top看栈顶是什么括号,配对成功才pop。
判断题:判断栈是否为空,只需看栈顶指针(或元素个数)是否为初始值。
考点:栈空判断(A5)。
解析:数组实现时栈顶指针 top == -1 即空栈(初始值);STL 用 st.empty()。✅ 正确
排除法:无(判断题)。混淆点:top == 0 表示有一个元素(下标 0)——不是空栈。
关联 · 出栈判空(H2):
pop/top前先判空是安全第一原则。
判断题:用定长数组实现栈时,push 前需要判断栈是否已满,否则会越界。
考点:栈满(A6)。
解析:定长数组实现栈,top == MAXN-1 时栈满;push 前不判满会越界写(未定义行为)。✅ 正确
排除法:无(判断题)。混淆点:链表实现栈没有"满"的概念(只受内存限制,见 F2)——"栈满"是数组实现特有的。
关联 · 数组栈 vs 链表栈(F2):数组栈要判满、链表栈不用——空间换简单的取舍。
判断题:检查括号是否匹配,可以用栈:遇到左括号入栈,遇到右括号与栈顶配对。
考点:括号匹配(B1)。
解析:扫描括号串:左括号入栈;右括号与栈顶左括号配对并弹出。全部扫描完栈空 = 匹配。✅ 正确
排除法:无(判断题)。混淆点:"最近未配对的左括号"就是栈顶——栈的 LIFO 特性天然匹配"就近配对"。
关联 · 不匹配判定(B7):右括号遇空栈 = 多余右括号;扫描结束栈非空 = 多余左括号。
判断题:表达式求值(如 1 + 2 * 3)需要按运算符优先级处理,可以用栈保存操作数和运算符。
考点:表达式求值(B2)。
解析:1 + 2 * 3 要先算乘法——用运算符栈暂存运算符、按优先级决定何时弹出计算;操作数栈存数值。✅ 正确
排除法:无(判断题)。混淆点:不能从左到右直接算(违反优先级);后缀表达式(G2)就是"不需要优先级判断"的形态。
关联 · 后缀表达式(G2):中缀转后缀(G3)后,计算只需一个操作数栈——栈是表达式处理的基石。
判断题:函数递归调用时,每层调用的局部变量和返回地址保存在系统栈中。
考点:递归栈(B3)。
解析:每层递归调用,系统把局部变量、参数、返回地址压入系统栈;返回时弹出恢复——递归 = 系统栈管理。✅ 正确
排除法:无(判断题)。混淆点:递归深度过大 → 系统栈溢出(栈溢出 Stack Overflow 的由来)。
关联 · 递归转栈(J5):递归可改写成"显式栈"模拟,避免系统栈深度限制。
判断题:浏览器的后退、编辑器的撤销(Undo)功能,本质是栈的应用。
考点:撤销操作(B4)。
解析:浏览器后退、编辑器 Undo 都是"最近的操作先撤销"——典型 LIFO,用栈保存历史。✅ 正确
排除法:无(判断题)。混淆点:队列场景是"先到先服务"(C6);"后做的先撤"必须栈。
关联 · 场景选择(F5):看"处理顺序":后进先出用栈、先进先出用队列。
判断题:十进制转二进制"除 2 取余"得到的余数序列需要逆序输出,适合用栈保存。
考点:进制转换(B5)。
解析:除 2 取余得到的余数先得到的是低位,输出要逆序——入栈暂存、出栈输出正好逆序。✅ 正确
排除法:无(判断题)。混淆点:不用栈也可以(先存数组再反着输出),但栈是最自然的"逆序"工具。
关联 · 栈输出序列(I6):
13 → 余 1,0,1,1 → 出栈 1101——栈的经典应用。
判断题:深度优先搜索(DFS)可以用栈实现(显式或递归)。
考点:DFS 用栈(B6)。
解析:深度优先 = "一条路走到黑再回头"——后访问的节点先扩展,天然 LIFO;递归版 DFS 靠系统栈、显式版用栈。✅ 正确
排除法:无(判断题)。混淆点:BFS 用队列(C7)——"深度优先栈、广度优先队列"是搜索的两大标配。
关联 · BFS 用队列(C7):DFS(栈)vs BFS(队列)——一个纵向深入、一个横向扩展。
判断题:扫描括号串时,若遇到右括号而栈为空,说明该右括号没有匹配的左括号。
考点:括号不匹配(B7)。
解析:右括号出现时栈空 = 这个右括号没有可配对的左括号(左括号不足或顺序错)。✅ 正确
排除法:无(判断题)。混淆点:扫描结束栈非空 = 左括号多余——两种不匹配:右多(中途空栈)与左多(结束非空)。
关联 · 括号匹配(B1):完整判匹配 = 中途遇空栈(右多)+ 结束栈非空(左多)双检查。
判断题:队列的特点是先进先出(FIFO)。
考点:先进先出(C1)。
解析:队列(queue)队头出、队尾进:先进先出(FIFO)——像排队买票。✅ 正确
排除法:无(判断题)。混淆点:栈是 LIFO(A1)——"栈 LIFO、队列 FIFO"必须成对记。
关联 · 队头队尾(C2):两端分工:进在尾、出在头。
判断题:队列的入队发生在队尾,出队发生在队头。
考点:队头队尾(C2)。
解析:入队(enqueue)在队尾,出队(dequeue)在队头——两端各干各的。✅ 正确
排除法:无(判断题)。混淆点:栈只有一端;队列两端分工——"一进一出、各守一端"。
关联 · 入队出队(C3):
push尾进、pop头出——顺序正好是"先来的先走"。
判断题:入队(push/enqueue)把元素加到队尾,出队(pop/dequeue)取出队头元素。
考点:入队出队(C3)。
解析:入队把元素加到队尾,出队取出队头元素并删除——FIFO 的落实。✅ 正确
排除法:无(判断题)。混淆点:STL 里 queue 的 push/pop 分别对应入队/出队,front 看队头、back 看队尾。
关联 · 队头队尾(C2):
front是"下一个要出队的",back是"刚进来的"。
判断题:判断队列是否为空,可看队头与队尾指针是否相同(或元素个数是否为 0)。
考点:队空判断(C4)。
解析:数组实现(front 指队头、rear 指队尾空位)时 front == rear 表示队空;或用计数器 cnt == 0。✅ 正确
排除法:无(判断题)。混淆点:顺序队列中 front == rear 只可能出现在空队(非循环时 rear ≥ front)。
关联 · 队空队满(D2):循环队列里
front == rear仍表示空——但判满要另想办法(浪费一格或计数)。
判断题:用定长数组实现队列时,push 前需要判断队列是否已满,否则会越界。
考点:队满判断(C5)。
解析:定长数组队列,rear 到数组末尾(且无法复用前面空间)时队满;push 前不判满会越界。✅ 正确
排除法:无(判断题)。混淆点:顺序队列的"满"可能是假溢出(前面有空位但 rear 到头,见 F4);循环队列才是真满。
关联 · 假溢出(F4):顺序队列 front 出队后前面空着,rear 却到头——"看似满、实则空"。
判断题:打印任务排队、银行叫号等"先到先服务"场景适合用队列。
考点:队列应用(C6)。
解析:打印任务、银行叫号、"先到先服务"——按到达顺序处理,就是 FIFO 队列。✅ 正确
排除法:无(判断题)。混淆点:后进先出的场景(撤销、括号)用栈——"先到先服务"是队列的名片。
关联 · BFS 用队列(C7):BFS 按"离起点由近到远"扩展,正是先到先服务。
判断题:广度优先搜索(BFS)用队列保存待访问节点。
考点:BFS 用队列(C7)。
解析:BFS 先访问的节点先扩展它的邻居——用队列保存"待访问"节点,保证按层推进。✅ 正确
排除法:无(判断题)。混淆点:DFS 用栈(B6)——两种搜索两种容器。
关联 · BFS 队列代码(K7):
queue + vis 标记是 BFS 标准三件套。
判断题:循环队列把数组首尾相接:队尾指针越界时回到数组开头(取模)。
考点:循环队列定义(D1)。
解析:循环队列让 rear/front 到数组末尾时取模回到开头——数组逻辑上首尾相接。✅ 正确
排除法:无(判断题)。混淆点:取模 % capacity 是循环的关键——忘记取模就退化成顺序队列(F4)。
关联 · 循环入队(D3):
rear = (rear + 1) % capacity——循环的本质。
判断题:循环队列中 front == rear 表示队空;为了区分队满,通常浪费一个存储单元。
考点:队空队满判定(D2)。
解析:front == rear 判空(无歧义);判满若也用 front == rear 会和空混淆——所以浪费一个空位:(rear + 1) % cap == front 判满。✅ 正确
排除法:无(判断题)。混淆点:另一种方案是计数器 cnt(L1)——用空间或计数二选一。
关联 · 容量计算(L7):浪费一格后,容量 N 的循环队列最多存 N-1 个。
判断题:循环队列入队:rear = (rear + 1) % capacity; q[rear] = x;
考点:循环入队(D3)。
解析:入队 = 写入队尾空位 + 队尾指针取模后移:q[rear] = x; rear = (rear + 1) % capacity;。✅ 正确
排除法:无(判断题)。混淆点:顺序是先存后移;(rear+1)%cap 越界回绕。
关联 · 循环队列结构(L1):取模让 rear 从末尾跳回开头——空间复用(D6)。
判断题:循环队列出队:front = (front + 1) % capacity;
考点:循环出队(D4)。
解析:出队 = 取队头元素 + 队头指针取模后移:x = q[front]; front = (front + 1) % capacity;。✅ 正确
排除法:无(判断题)。混淆点:front 后移意味着"该位置以后可被新元素覆盖"。
关联 · 出队取模(L3):front 也要取模——头尾指针移动规则完全对称。
循环队列容量为 ,front = 2、rear = 5,队列中有( )个元素。
考点:循环队列长度(D5)。
解析:长度 = (rear - front + capacity) % capacity:(5 - 2 + 8) % 8 = 3。✅ B
排除法:A 忘了加 capacity 直接减(负数);C 是 rear 值;D 是容量。
关联 · 长度公式(L5):公式
(rear-front+cap)%cap对 front > rear(绕回)的情况同样成立。
验算: ✓
判断题:循环队列避免了顺序队列的"假溢出"——队头出队后空出的空间可以继续使用。
考点:循环队列好处(D6)。
解析:顺序队列出队后前面空间被浪费(假溢出);循环队列让 front 走过的位置被 rear 重新使用——空间复用。✅ 正确
排除法:无(判断题)。混淆点:循环队列不是"扩容"而是"复用"——容量不变,利用率提高。
关联 · 循环 vs 顺序(D7):循环队列 = 顺序队列 + 取模回绕——解决假溢出。
判断题:顺序队列出队后 front 后移,数组前面的空间被浪费;循环队列让这些空间复用。
考点:循环 vs 顺序(D7)。
解析:顺序队列出队只移动 front,前面空间永远空着(直到 rear 到头"假满");循环队列 front 空出的位置 rear 能绕回来用。✅ 正确
排除法:无(判断题)。混淆点:循环队列的"循环"是逻辑上的(取模),物理还是同一个数组。
关联 · 假溢出(F4):假溢出是顺序队列的毛病,循环队列是它的解药。
判断题:函数调用时,参数、局部变量、返回地址依次压入系统调用栈;函数返回时按相反顺序弹出——递归的执行过程就是栈的典型应用。
考点:栈与函数调用栈(E1)。
解析:函数调用时参数、局部变量、返回地址压入系统调用栈,返回时弹出——递归就是靠调用栈工作的(第 14 章)。✅ 正确
排除法:无(判断题)。混淆点:调用栈溢出(递归过深)是"后进先出"结构的副作用。
关联 · 栈的基本概念(A1):调用栈是栈的隐藏应用。
判断题:编辑器的撤销(undo)功能可以用栈实现:每步操作压栈,撤销时弹栈回退到上一步。
考点:栈与撤销操作(E2)。
解析:undo 用栈:每步操作压栈、撤销弹栈——"最后做的先撤销"正是 LIFO。✅ 正确
排除法:无(判断题)。混淆点:重做(redo)需要另一个栈保存被撤销的操作。
关联 · 栈的应用(B 组):撤销是栈应用的又一实例。
判断题:银行叫号、打印任务排队等场景用队列模拟:先到达的先服务(FIFO),新任务排在队尾。
考点:队列与排队系统(E3)。
解析:银行叫号、打印队列用队列模拟:先到先服务(FIFO),新任务入队尾。✅ 正确
排除法:无(判断题)。混淆点:队列模拟的关键是"顺序不乱"——用数组 + 头尾指针即可。
关联 · 队列的基本概念(C1):FIFO 是排队系统的天然模型。
判断题:BFS 用队列保存待访问节点——先进先出保证按层扩展,这是队列在算法中的典型应用。
考点:队列与广度优先搜索(E4)。
解析:BFS 用队列保存待访问节点,先进先出保证按层扩展(第 16 章)。✅ 正确
排除法:无(判断题)。混淆点:把 BFS 的队列换成栈就变成 DFS 的顺序(第 16 章 P4)。
关联 · 队列的适用场景(F5):BFS 是队列最重要的算法应用。
判断题:可以用两个栈实现队列,也可以用两个队列实现栈。
考点:互相实现(E5)。
解析:两个栈可实现队列(入队压 A、出队把 A 倒入 B 再弹 B);两个队列可实现栈(入栈入队,出栈把前 n-1 个倒腾到另一队列)。✅ 正确
排除法:无(判断题)。混淆点:两栈实现队列每个元素最多倒一次(均摊 ,见 M6);两队列实现栈出栈是 。
关联 · 两栈实现队列(M6):经典面试题——"倒腾"思想。
判断题:栈适合"后进先出"的匹配与回溯场景(括号匹配、DFS),队列适合"先进先出"的排队与扩展场景(BFS)——按场景选结构。
考点:栈与队列的场景选择(E6)。
解析:匹配/回溯(括号匹配、DFS)用栈;排队/扩展(BFS、任务调度)用队列——按场景选结构。✅ 正确
排除法:无(判断题)。混淆点:选错结构程序也能写,但思路会别扭、代码变复杂。
关联 · 栈 vs 队列场景选择(F5):同主题的代码版视角。
判断题:栈和队列的插入、删除、取顶/取头操作都是 。
考点:操作复杂度(F1)。
解析:栈/队列的插入、删除、查看顶端/队头都是常数时间 (只动一端或两端指针)。✅ 正确
排除法:无(判断题)。混淆点: 指"已知位置"的操作——队列/栈天然只在一端操作,所以全部 。
关联 · STL 容器(F6):
stack/queue/deque的主要操作都是 ——这也是它们被大量使用的原因。
判断题:数组实现栈需预分配大小(可能栈满),链表实现栈不设上限(只受内存限制)。
考点:数组栈 vs 链表栈(F2)。
解析:数组栈预分配大小(可能栈满、可能浪费);链表栈按需 new 节点(只受内存限制,不会"满")。✅ 正确
排除法:无(判断题)。混淆点:链表栈的节点有指针开销,数组栈紧凑——"有界高效 vs 无界灵活"。
关联 · 栈满(A6):数组栈要判满(A6);链表栈天然免判——但注意内存耗尽。
判断题:链表实现队列需维护队头队尾两个指针,入队出队才能都做到 。
考点:数组队列 vs 链表队列(F3)。
解析:链表队列必须维护队头、队尾两个指针:出队动头、入队动尾,才都 ;只留头指针则入队要 。✅ 正确
排除法:无(判断题)。混淆点:单链表队列若只有 head,尾插要遍历()——tail 指针是 入队的保证。
关联 · 链表实现队列(第 7 章 F2):头删尾插 + 尾指针 = 链表队列标准形态。
判断题:顺序队列中,出队后 front 后移,即使数组前面有空位,rear 到末尾时也"看似满"——这叫假溢出。
考点:假溢出(F4)。
解析:顺序队列 front 不断后移,数组前面空出一片,rear 到末尾时判"满"——但实际空间没用完,叫假溢出。✅ 正确
排除法:无(判断题)。混淆点:真溢出 = 空间真的用完;假溢出 = 前面有空位却用不上(非循环队列的缺陷)。
关联 · 循环队列(D6):循环队列让 front 腾出的空间可复用——假溢出消失。
下列场景更适合用栈的是( )。
考点:场景选择(F5)。
解析:括号匹配 = 最近配对(LIFO)→ 栈。✅ A
排除法:B 打印排队、C BFS、D 银行叫号都是"先到先服务"→ 队列。
关联 · 栈应用(B 组)/队列应用(C 组):判断标准一句话:后进先出用栈、先进先出用队列。
判断题:C++ STL 的 stack 和 queue 是容器适配器,push/pop/top(front)等操作都是 ,是 CSP-J 最常用的两个容器。
考点:STL 的栈与队列(F6)。
解析:stack/queue 是容器适配器,push/pop/top(front)都是 ——CSP-J 最常用的两个容器。✅ 正确
排除法:无(判断题)。混淆点:二者底层都基于 vector 等容器,但对使用者只暴露栈/队列接口。
关联 · STL 模板(第 4 章 E 组):C++ 标准库容器体系。
判断题:括号串 (()()) 是匹配的,()) 是不匹配的。
考点:括号匹配综合(G1)。
解析:(()()):每个右括号都有可配的左括号、结束栈空 → 匹配;()):第二个右括号时栈已空 → 不匹配。✅ 正确
排除法:无(判断题)。混淆点:数左右括号个数相等 ≠ 匹配——())(() 个数相等但不匹配(顺序错)。
关联 · 括号匹配(B1):匹配 = 过程不空栈 + 结束栈空——"个数相等"只是必要条件。
后缀表达式 3 4 + 5 * 的值是( )。
考点:后缀表达式(G2)。
解析:后缀 3 4 + 5 *:遇 + 弹 3、4 得 7 压回;遇 * 弹 7、5 得 35。✅ A
排除法:B 是 (3+4)*5 记成 47;C 是 3+4*5(中缀优先级算错);D 是 3*4*5。
关联 · 后缀求值(N7):后缀求值只需一个栈:数字入栈、运算符弹两个算完压回。
验算:, ✓
中缀表达式 a + b * c 转后缀为( )。
考点:中缀转后缀(G3)。
解析:a + b * c:* 优先级高于 +,先算 b*c——后缀 a b c * +。✅ A
排除法:B a b + c * 是先算加法(错);C a b c + * 是 a*(b+c);D 顺序乱。
关联 · 运算符优先级(第 4 章 C7):
*//高于+/-——转后缀时优先级决定运算符出栈时机。
判断题:逆波兰表达式就是后缀表达式(运算符写在操作数后面)。
考点:逆波兰(G4)。
解析:逆波兰表达式(RPN)= 后缀表达式:运算符写在操作数之后,如 3 4 + 表示 。✅ 正确
排除法:无(判断题)。混淆点:前缀表达式运算符在前(+ 3 4);中缀在中间——三种记法。
关联 · 后缀求值(G2):逆波兰无需括号和优先级——计算机求值首选。
判断题: 个元素按顺序入栈,出栈序列的种数是卡特兰数。
考点:卡特兰数(G5)。
解析: 个元素按顺序入栈(任意时刻可出栈),合法出栈序列的种数是第 个卡特兰数 。✅ 正确
排除法:无(判断题)。混淆点: 时合法序列 5 种(123,132,213,231,321),恰是 。
关联 · 出栈序列合法性(J7):合法序列判定:出栈序列中"大的先出"必须满足栈内顺序约束。
判断题:配对 ()、[]、{} 多种括号时,右括号必须与最近未配对的左括号同类型。
考点:多种括号(G6)。
解析:([)] 中 ] 的栈顶是 (——类型不匹配 → 非法;右括号必须与**栈顶(最近未配对)**的左括号同类型。✅ 正确
排除法:无(判断题)。混淆点:只数个数不看类型会误判 ([)]——"就近 + 同型"双条件。
关联 · 括号匹配代码(J1):
]与栈顶[比较、}与{比较——类型检查在配对时做。
判断题:数组实现栈时,栈顶指针常初始化为 (空栈),push 时先 top++。
考点:栈顶初始化(H1)。
解析:空栈 top = -1;push 先 ++top 再存(stk[++top] = x)——第一个元素存到下标 0。✅ 正确
排除法:无(判断题)。混淆点:top = 0 初始化则 push 先存再 top++(另一种约定)——两种约定都行,但 -1 更常见。
关联 · push 代码(I2):
stk[++top] = x是"先移指针再存"的标准写法。
判断题:pop 前必须判断栈是否为空,空栈 pop 是错误操作(下溢)。
考点:出栈判空(H2)。
解析:空栈 pop 是下溢(underflow)——数组实现会越界读、STL 是未定义行为;pop/top 前必须判空。✅ 正确
排除法:无(判断题)。混淆点:下溢 ≠ 上溢(栈满 push)——"空栈弹出、满栈压入"都是错误。
关联 · 栈空判断(A5):
while (!st.empty())循环处理是常见安全模式。
判断题:数组实现队列时,常用约定:rear 指向队尾元素的下一个空位,front 指向队头元素。
考点:rear 指向空位(H3)。
解析:常用约定:rear 指向队尾元素的下一个空位,front 指向队头元素——入队先写 rear 再后移。✅ 正确
排除法:无(判断题)。混淆点:另一种约定 rear 指向队尾元素本身——判空判满公式随之不同,约定要自洽。
关联 · 入队代码(K2):
q[rear++] = x——写空位、指针后移,两步合一。
判断题:循环队列的指针移动都要对容量取模(%),否则会越界。
考点:循环队列取模(H4)。
解析:循环队列的 front/rear 移动都必须 % capacity——否则指针越界,循环失效(退化成顺序队列)。✅ 正确
排除法:无(判断题)。混淆点:取模遗漏是循环队列最经典的 bug(见 P3)。
关联 · 取模遗漏(P3):
front++忘写% MAXN→ 指针越界——"凡移动必取模"。
下列说法错误的是( )。
考点:综合判断(H5)。
解析:D 错误——栈的插入(push)发生在栈顶,不是栈底;栈底是固定的。✅ D
排除法:A 栈 LIFO ✓;B 队列 FIFO ✓;C 循环队列复用空间 ✓——A/B/C 都正确。
关联 · 本章串联:A(A1)、B(C1)、C(D6)、D(A2)——综合题 = 细节判断的集合。
01const int MAXN = 100; 02int stk[MAXN]; // 用数组模拟栈 03int top = -1; // 栈顶下标:-1 表示空栈
判断题:push 前先 top++ 再存值,pop 后 top--。
考点:栈数组结构(I1)。
解析:数组模拟栈:stk[] 存元素、top 是栈顶下标;top = -1 表示空栈。push:stk[++top] = x(先移指针再存);pop:stk[top--](先取再降)。✅ 正确
实现要点:数组模拟栈 = 一个数组 + 一个 top 指针:top 指向栈顶元素下标,-1 表示空栈;push 先 ++top 再存、pop 先取再 top--——"指针 + 数组"就是栈的全部家当,STL 的 stack 只是封装了这套逻辑。手算时把 top 想成"栈顶手指"。
排除法:无(判断题)。混淆点:top 既是"指针"也是"元素个数 - 1"——top+1 就是当前元素个数。
关联 · 栈顶初始化(H1):
-1初始化让第一个元素落在下标 0——约定自洽即可。
01const int MAXN = 100; 02int stk[MAXN]; 03int top = -1; // 栈顶下标,-1 表示空栈 04 05// 入栈:把 x 压入栈顶 06void push(int x) { 07 stk[++top] = x; 08}
判断题:stk[++top] = x; 是先移动栈顶指针,再存入元素。
考点:push 代码(I2)。
解析:stk[++top] = x:前缀 ++ 先让 top 指向新空位,再存入——顺序正确(先移指针、后写数据)。✅ 正确
实现要点:push 的"先移后存"(stk[++top] = x)保证新元素落在栈顶之上、不覆盖旧数据——前缀 ++ 先加后用。与 pop 的"先取后降"(stk[top--])成对记忆,栈操作永不出错。
排除法:无(判断题)。混淆点:若写 stk[top++] = x 则覆盖当前栈顶(数据丢失,见 P1)。
关联 · 栈顶更新(P1):push 忘更新 top = 反复覆盖——"先移后存"是铁律。
01const int MAXN = 100; 02int stk[MAXN]; 03int top = -1; // 栈顶下标,-1 表示空栈 04 05// 出栈:弹出栈顶元素 06int pop() { 07 return stk[top--]; 08}
判断题:return stk[top--]; 是先取栈顶元素,再下移栈顶指针。
考点:pop 代码(I3)。
解析:return stk[top--]:后缀 -- 先取当前栈顶值,再让 top 下移——取出的值不丢、指针正确回落。✅ 正确
实现要点:pop 的"先取后降"(return stk[top--])保证取到的是当前栈顶、指针正确回落——后缀 -- 先用后减。若写 stk[--top] 会跳过栈顶元素(取到下面一个)。
排除法:无(判断题)。混淆点:stk[--top] 会跳过栈顶元素(取到下面一个)。
关联 · 出栈判空(H2):正式代码里 pop 前要判
top == -1(本题略去判空聚焦写法)。
01// 判断括号串是否匹配(只有小括号) 02bool check(const string &s) { 03 int top = 0; 04 char stk[100]; 05 for (char c : s) { 06 if (c == '(') stk[top++] = c; // 左括号入栈 07 else { // 右括号 08 if (top == 0) return false; // 栈空:没有左括号可配 09 top--; // 弹出配对的左括号 10 } 11 } 12 return top == 0; // 栈空 = 全部配对 13}
判断题:check("(()())") 返回 true,check("())") 返回 false。
考点:括号匹配代码(I4)。
解析:(()()):过程不空栈、结束 top==0 → true;()):第二个右括号时 top==0 → return false。✅ 正确
实现要点:括号匹配算法三步:① 左括号入栈;② 右括号:栈空 → 返回 false(无左可配),否则弹出栈顶配对;③ 扫描结束栈空才返回 true(防左括号多余)。核心是"栈顶 = 最近未配对的左括号"——LIFO 与"就近配对"天然吻合。
排除法:无(判断题)。混淆点:(()() 结束 top=1 非空 → false(左括号多)——两种失败路径代码都覆盖。
关联 · 括号匹配(B1):这段代码是 B1 的数组版实现——"入栈/配对弹出/双失败检查"三步。
// 依次执行:push(1) push(2) push(3) pop() pop() push(4) pop() // 每次 pop 输出弹出的值,输出为( )。
考点:入栈出栈序列(I5)。
解析:push 1,2,3 → 栈 [1,2,3];pop → 3;pop → 2;push 4 → [1,4];pop → 4。输出 3 2 4。✅ A
实现要点:手算 push/pop 序列 = 画栈图:把栈画成一列,push 在顶上加元素、pop 从顶上去掉并记录——每一步都画,最后串起 pop 记录即答案。这是读一切栈代码的通用方法。
排除法:B 1 2 3 是队列顺序;C 3 2 1 漏了 push 4;D 4 2 3 顺序乱。
关联 · 栈模拟(J4):逐行模拟栈变化是阅读程序基本功——画栈图最稳。
验算:栈:[1,2,3]→[1,2]→[1]→[1,4]→[1];出 3,2,4 ✓
// 十进制 13 转二进制:不断除以 2 取余,余数依次为 1, 0, 1, 1 // 把余数依次入栈后,依次出栈输出为( )。
考点:进制转换栈(I6)。
解析:13 ÷ 2 余 1、6 ÷ 2 余 0、3 ÷ 2 余 1、1 ÷ 2 余 1——余数从低位到高位:1,0,1,1;入栈后出栈逆序 → 1101。✅ A
实现要点:除基取余法:n 不断 ÷2 取余,余数从低位到高位产生;入栈再出栈 = 逆序输出成高位到低位——"后得的先出"正是栈的用武之地。十进制转任意进制同理(÷k 取余)。
排除法:B 1011 是入栈顺序(没出栈);C 1010 是 10 的二进制;D 1110 是 14 的二进制。
关联 · 进制转换(B5):除基取余 + 栈逆序——十进制转二进制的标准流程。
验算: ✓
01// 判断括号串是否匹配(含 () [] {} 三种括号) 02bool check(const string &s) { 03 stack<char> st; 04 for (char c : s) { 05 if (c == '(' || c == '[' || c == '{') st.push(c); 06 else { 07 if (st.empty()) return false; 08 char t = st.top(); st.pop(); 09 if (c == ')' && t != '(') return false; 10 if (c == ']' && t != '[') return false; 11 if (c == '}' && t != '{') return false; 12 } 13 } 14 return st.empty(); 15}
判断题:check("([)]") 返回 false。
考点:括号匹配完整代码(J1)。
解析:([)]:[ 入栈、( 入栈;遇到 ) 弹出 ( 配对 ✓;遇到 ] 弹出栈顶 ( ≠ [ → return false。✅ 正确
实现要点:多类型括号匹配 = 单类型框架 + 类型检查:右括号弹出栈顶后必须与它同型()配(、]配[、}配{)——弹栈前先判空,防止对空栈取 top。([)] 就是"个数够但类型错"的典型反例。
排除法:无(判断题)。混淆点:([)] 每对括号都"出现"了但不匹配——类型顺序错误,靠栈顶同型检查抓出。
关联 · 多种括号(G6):三种括号的匹配 = 入栈 + 同型弹出 + 空栈检查。
// 后缀表达式求值:遇数字入栈,遇运算符弹出两个数计算后压回 // 对后缀表达式 "5 3 + 2 *" 求值,结果是( )。
考点:后缀表达式求值(J2)。
解析:5、3 入栈;+ 弹 3、5 得 8 压回;2 入栈;* 弹 2、8 得 16。✅ A
实现要点:后缀求值算法 = 遇数字入栈;遇运算符弹两个(先弹的是右操作数 b、后弹的是左操作数 a)、算完压回——"弹二算一压回"三步循环,结束时栈顶即答案。减/除时操作数顺序错则全错。
排除法:B 是 5+3*2 中缀错算;C 是 5+3+2+3;D 是 5+3+2。
关联 · 后缀求值(N7):数字入栈、运算符弹二算一压回——后缀求值模板。
验算: ✓
中缀转后缀的规则:数字直接输出;运算符与栈顶比较,优先级不低于栈顶才入栈
中缀 a + b * c - d 转后缀,结果是( )。
考点:中缀转后缀(J3)。
解析:a + b * c - d:a 输出;+ 入栈;b 输出;* 优先级高于 + 入栈;c 输出;- 与 + 同级,弹出 *、+ 再入栈;d 输出;结束弹出 -。→ a b c * + d -。✅ A
实现要点:中缀转后缀 = 操作数直接输出 + 运算符栈:新运算符优先级 不高于 栈顶时先弹出栈顶(直到栈顶优先级更低)再入栈;左括号直接入栈、右括号弹出到左括号为止。优先级比较是核心,同级运算符按先来后到弹出。
排除法:B 把 + 先算((a+b)*c-d);C 是 a+(b+c)*d;D 顺序乱。
关联 · 优先级(第 4 章 C7):转后缀核心 = 运算符栈:优先级低/同级 → 先弹出栈顶。
验算::先乘后加减 → 后缀a b c * + d -✓
依次执行:push(1) push(2) pop() push(3) push(4) pop() pop() pop()
每次 pop 输出弹出的值,输出为( )。
考点:栈模拟输出(J4)。
解析:push1,2 → [1,2];pop → 2;push3,4 → [1,3,4];pop pop pop → 4,3,1。输出 2 4 3 1。✅ A
实现要点:长序列栈模拟 = 画栈图逐行推进(同 I5):每步标注"压入 X / 弹出 Y",特别注意连续 pop 时栈内残留顺序——"后进先出"在每一步都成立。
排除法:B 队列序;C 2 3 4 1 少一次 pop;D 全逆序。
关联 · 栈模拟(I5):长序列逐行画栈——"压入/弹出"同步标注。
验算:[1,2]→[1]→[1,3,4]→[1,3]→[1]→[];出 2,4,3,1 ✓
判断题:递归程序可以用"显式栈"模拟(把每层的状态压栈),避免递归深度过大导致的栈溢出。
考点:递归转栈(J5)。
解析:递归每层状态(参数、局部变量、返回点)可显式压入自建栈,循环模拟——避免系统栈深度限制(栈溢出)。✅ 正确
实现要点:递归转迭代 = 用显式栈存"每层的状态"(参数、局部变量、返回点),循环内按状态机推进——把系统栈做的事用自建栈做一遍。深度可控(不爆系统栈),但代码更繁琐。
排除法:无(判断题)。混淆点:显式栈模拟递归 = 手动管理"调用栈"——空间 不变,但可控。
关联 · 递归栈(B3):递归靠系统栈;改显式栈 = 自己当系统栈的管理员。
判断题:表达式 1 + 2 * 3 求值时,先算 2 * 3 再算加法——运算符栈按优先级决定何时弹出运算。
考点:表达式求值(J6)。
解析:1 + 2 * 3:2 * 3 先算——运算符栈中,新运算符优先级不高于栈顶时,先弹出栈顶运算。✅ 正确
实现要点:中缀表达式求值 = 操作数栈 + 运算符栈:数字压操作数栈;运算符与栈顶比较,优先级不高于栈顶时先弹出运算(高优先级先算),结果压回——与转后缀(J3)是同一套优先级规则的两面。
排除法:无(判断题)。混淆点:从左到右直接算 = 违反优先级;栈让"高优先级先算"自动发生。
关联 · 中缀转后缀(J3):求值与转后缀是同一套栈规则的两面。
1、2、3 依次入栈(可在任意时刻出栈),下列哪个是【不合法】的出栈序列?( )。
考点:出栈序列合法性(J7)。
解析:3 1 2 非法:3 出栈时 1、2 都在栈内(顺序 1,2),之后 2 必须先于 1 出——3 2 1 才合法。✅ B
实现要点:判断出栈序列合法性 = 模拟法:按入栈序逐个压入,只要栈顶等于目标序列的当前元素就弹出(目标指针后移);全部弹完 = 合法。3 1 2 非法因为 3 出栈时 2 还压在 1 上面。
排除法:A 2 1 3(push1 push2 pop pop push3 pop)合法;C 1 3 2(push1 pop push2 push3 pop pop)合法;D 3 2 1(全入再全出)合法——只有 B 非法。
关联 · 卡特兰数(G5): 合法序列 5 种,
3 1 2恰是唯一的非法排列之一。
验算:模拟:1 入、2 入、3 入、3 出 → 栈 [1,2],此时要出 1 必须先出 2 →3 1 2不可能 ✓
01const int MAXN = 100; 02int q[MAXN]; // 用数组模拟队列 03int front = 0; // 队头下标:指向队头元素 04int rear = 0; // 队尾下标:指向队尾元素的下一个空位
判断题:该约定下,front == rear 表示队列为空。
考点:队列数组结构(K1)。
解析:front 指队头元素、rear 指队尾下一个空位;初始 front == rear == 0,front == rear 即空队。✅ 正确
实现要点:数组队列 = 数组 + front/rear 两个指针:front 指队头元素、rear 指队尾下一个空位;front == rear 判空——"一头一尾"是队列的标配,STL queue 内部也是这套。
排除法:无(判断题)。混淆点:rear 指向"空位"是约定——若指向队尾元素,判空/入队写法都不同。
关联 · rear 指向空位(H3):
q[rear++] = x入队、q[front++]出队——两个指针各司其职。
01const int MAXN = 100; 02int q[MAXN]; 03int front = 0, rear = 0; // front 队头、rear 队尾下一个空位 04 05// 入队:把 x 加到队尾 06void push(int x) { 07 q[rear++] = x; 08}
判断题:q[rear++] = x; 是把元素存入队尾空位,然后队尾指针后移。
考点:入队代码(K2)。
解析:q[rear++] = x:把 x 写入 rear 指向的空位,然后 rear 后移——入队只动队尾。✅ 正确
实现要点:入队 = 写 rear 空位 + rear++(q[rear++] = x):rear 是"写入指针",永远指向下一个空位——入队只动队尾,队头纹丝不动。
排除法:无(判断题)。混淆点:误写 q[front++] 会把元素插到队头(破坏 FIFO,见 P4)。
关联 · 入队(C3):队尾进——rear 是"写入指针"。
01const int MAXN = 100; 02int q[MAXN]; 03int front = 0, rear = 0; // front 队头、rear 队尾下一个空位 04 05// 出队:取出队头元素 06int pop() { 07 return q[front++]; 08}
判断题:return q[front++]; 是先取队头元素,再后移队头指针。
考点:出队代码(K3)。
解析:return q[front++]:先取 front 指向的队头元素,再后移 front——出队只动队头。✅ 正确
实现要点:出队 = 取 front 元素 + front++(return q[front++]):front 是"读取指针",指向下一个要出的元素——出队只动队头。两句合记:"进尾出头"。
排除法:无(判断题)。混淆点:q[rear--] 变成从队尾出(栈行为)——front/rear 混淆是经典错误(P4)。
关联 · 出队(C3):队头出——front 是"读取指针"。
01const int MAXN = 4; 02int q[MAXN]; 03int front = 0, rear = 0; 04 05// 依次执行:push(1) push(2) push(3) pop() push(4) 06// 此时 front = 1, rear = 4
判断题:rear 已到数组末尾,但前面 q[0] 空着——这就是顺序队列的"假溢出"。
考点:假溢出代码(K4)。
解析:push 1,2,3 后 rear=3;pop 后 front=1;push 4 后 rear=4——rear 到末尾,但 q[0] 空着:顺序队列"看似满、实有空位"= 假溢出。✅ 正确
实现要点:顺序队列 front 只增不减 → 出队腾出的空间永远废弃 → rear 一到头就"假满"——理解假溢出 = 理解循环队列为何存在(D6 用取模复用空间)。
排除法:无(判断题)。混淆点:真满 = 4 个位置全占满;这里只有 3 个元素(q[1],q[2],q[3]),q[0] 可复用。
关联 · 假溢出(F4):假溢出的解法 = 循环队列(取模复用 q[0])。
依次执行:push(1) push(2) push(3) pop() push(4) pop() pop() pop()
每次 pop 输出弹出的值,输出为( )。
考点:队列模拟输出(K5)。
解析:push1,2,3 → [1,2,3];pop → 1;push4 → [2,3,4];pop pop pop → 2,3,4。输出 1 2 3 4。✅ A
实现要点:手算队列操作 = 画队形图(左头右尾):push 在右边加、pop 从左边取——FIFO 一目了然,出队记录串起来即答案。
排除法:B 栈序;C 1 2 4 3 顺序乱;D 少了 1。
关联 · 队列模拟(O2):FIFO——先入先出,画队形图最直观。
验算:[1,2,3]→[2,3]→[2,3,4]→[3,4]→[4]→[];出 1,2,3,4 ✓
约瑟夫问题用队列模拟:队头出队、报数不为 k 的重新入队(到队尾),报数为 k 的出列
n = 5, k = 2(报到 2 出列),出列顺序是( )。
考点:约瑟夫队列模拟(K6)。
解析:队列模拟约瑟夫:队头出队,报数不为 2 的重新入队(到队尾),报到 2 的出列:2 出 → 4 出 → 1 出 → 5 出 → 剩 3。✅ A
实现要点:约瑟夫队列版 = 队头出队 → 报数不中的重新入队(到队尾,形成"圆圈")、报数命中的出列——"出队再入队"用队列模拟围圈,报数用计数器。
排除法:B 顺序出;C/D 报数错位。
关联 · 约瑟夫(第 7 章 M4):循环链表与队列都能模拟约瑟夫——队列版"出队再入队"实现循环。
验算:队列 [1,2,3,4,5]:1 转、2 出、3 转、4 出、5 转、1 出、3 转、5 出、3 剩 ✓
01// BFS 模板:起点入队,不断出队访问并把未访问的邻居入队 02queue<int> q; 03q.push(start); 04vis[start] = true; 05while (!q.empty()) { 06 int u = q.front(); q.pop(); 07 for (int v : 邻接表[u]) 08 if (!vis[v]) { vis[v] = true; q.push(v); } 09}
判断题:BFS 按"离起点由近到远"的顺序访问节点。
考点:BFS 队列代码(K7)。
解析:BFS 用队列:起点入队,出队访问,未访问邻居入队——按"离起点距离"分层推进。✅ 正确
实现要点:BFS 模板 = 起点入队 + vis 标记(入队时标记防重复入队)→ 循环:出队访问 → 未访问邻居入队并标记——队列保证"离起点近的先扩展",即按层推进。
排除法:无(判断题)。混淆点:vis 在入队时标记(不是出队时)——防重复入队。
关联 · BFS 用队列(C7):queue + vis + 邻接表 = BFS 标准三件套。
01const int MAXN = 8; 02int q[MAXN]; 03int front = 0, rear = 0; // 循环队列:指针移动都 % MAXN 04int cnt = 0; // 当前元素个数
判断题:用 cnt 计数时,队空为 cnt == 0,队满为 cnt == MAXN,不需要浪费空间。
考点:循环队列结构(L1)。
解析:用计数器 cnt 的循环队列:队空 cnt == 0、队满 cnt == MAXN——不需要浪费空位,空间利用率 100%。✅ 正确
实现要点:循环队列 = 顺序队列 + 取模回绕:指针到末尾 % MAXN 回开头;用 cnt 计数时队空 cnt==0、队满 cnt==MAXN——不浪费空间,多一个变量而已。
排除法:无(判断题)。混淆点:浪费一格方案(L4)与计数方案(L1)二选一——计数更省空间但多一个变量。
关联 · 队空队满(D2):
front == rear在计数方案下只判空不判满——两种方案公式不同。
01const int MAXN = 8; 02int q[MAXN]; 03int front = 0, rear = 0; 04 05// 循环队列入队(用 cnt 计数判满) 06void push(int x) { 07 q[rear] = x; 08 rear = (rear + 1) % MAXN; // 队尾指针后移并取模 09 cnt++; 10}
判断题:rear = (rear + 1) % MAXN 让 rear 越界时回到数组开头。
考点:循环入队取模(L2)。
解析:rear = (rear + 1) % MAXN:rear 到 MAXN-1 后 +1 变 MAXN,取模回 0——数组首尾相接。✅ 正确
实现要点:循环入队 = 写 rear 位 + rear = (rear + 1) % MAXN:取模让指针"绕圈",数组逻辑上首尾相接——"凡移动必取模"是循环队列铁律。
排除法:无(判断题)。混淆点:忘取模 = rear 越界(P3)——取模是"循环"的灵魂。
关联 · 循环入队(D3):入队三步:写空位、指针取模后移、cnt++(计数方案)。
01const int MAXN = 8; 02int q[MAXN]; 03int front = 0, rear = 0; 04 05// 循环队列出队 06int pop() { 07 int x = q[front]; 08 front = (front + 1) % MAXN; // 队头指针后移并取模 09 cnt--; 10 return x; 11}
判断题:front = (front + 1) % MAXN 出队后队头指针绕回。
考点:循环出队取模(L3)。
解析:front = (front + 1) % MAXN:front 同样取模回绕——头尾指针移动规则对称。✅ 正确
实现要点:循环出队 = 取 front 位 + front = (front + 1) % MAXN:头尾指针移动规则完全对称,都取模——front 腾出的位置供 rear 复用。
排除法:无(判断题)。混淆点:front 后移 = 该位置元素出队,空间留给后续入队(复用)。
关联 · 循环出队(D4):出队三步:取队头、指针取模后移、cnt--。
01const int MAXN = 8; 02int q[MAXN]; 03int front = 0, rear = 0; // 浪费一个空位的循环队列 04 05// 队空:front == rear;队满:(rear + 1) % MAXN == front
判断题:容量为 的循环队列(浪费一格),最多能存 个元素。
考点:队空队满判断(L4)。
解析:浪费一格方案:front == rear 判空、(rear + 1) % MAXN == front 判满——容量 8 最多存 7 个(8-1)。✅ 正确
实现要点:浪费一格方案:front == rear 判空、(rear + 1) % N == front 判满——永远留一个空位,让"满"和"空"不会撞车(都满足 front==rear 就无法区分)。
排除法:无(判断题)。混淆点:不浪费一格的话"满"与"空"都满足 front==rear,无法区分——浪费一格是经典解法。
关联 · 容量计算(L7): 的数组、 的容量——"少存一个,换来判满无歧义"。
01const int MAXN = 8; 02// 循环队列:front = 6, rear = 2,元素个数 = (rear - front + MAXN) % MAXN
队列中有( )个元素。
考点:循环队列长度(L5)。
解析:长度 = (rear - front + MAXN) % MAXN = (2 - 6 + 8) % 8 = 4 % 8 = 4。✅ A
实现要点:循环队列长度公式 (rear - front + N) % N:加 N 再取模,处理 front > rear(绕回)的情况——手算时画个环形图,数 front 到 rear 顺时针的格数。
排除法:B 是 rear - front 直接算(负数出错);C 是 front 值;D 无来源。
关联 · 长度公式(D5):
(rear - front + cap) % cap对绕回情况(front > rear)同样成立。
验算: ✓
01const int MAXN = 4; // 循环队列,浪费一格,最多存 3 个 02int q[MAXN]; 03int front = 0, rear = 0; 04 05// 依次执行:push(1) push(2) push(3) pop() pop() push(4) 06// 执行完后,队列中从队头到队尾的元素是( )。
考点:循环队列模拟(L6)。
解析:push1,2,3(满:3 个)→ pop 1、pop 2 → push4(写入 q[3],rear 绕回 0)→ 队列 [3,4]。✅ A
实现要点:循环队列手算 = 画环形数组:front/rear 沿环走,rear 绕回后写入的位置正是之前出队腾出的前端空位——这就是"空间复用"的直观体现。
排除法:B 只剩 1、2(没算 push4);C 含 5(没 push 5);D 队头队尾颠倒。
关联 · 循环队列好处(D6):pop 腾出 q[0],q[1] 后,rear 绕回——空间复用的直观体现。
验算:q[2]=3, q[3]=4, front=2, rear=0:从 front 到 rear 走取模:3, 4 ✓
判断题:循环队列数组大小 ,浪费一格判满时,队列实际最多容纳 个元素。
考点:容量计算(L7)。
解析:浪费一格判满时,数组 个位置、 个可存数据(永远留一个空位区分空/满)。✅ 正确
实现要点:浪费一格方案容量 = N-1(永远留一格空位判满);计数方案容量 = N——两种方案容量差 1,判满公式不同,考试先看清用的是哪种。
排除法:无(判断题)。混淆点:计数方案(L1)可存满 个——两种方案容量差 1。
关联 · 队空队满(D2): 数组 + 浪费一格 → 容量 ;"满 = 只剩一个空位"。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 string s = "abcba"; 05 stack<char> st; 06 for (char c : s) st.push(c); // 全部压栈 07 bool ok = true; 08 for (char c : s) { // 弹栈顺序 = 逆序,与正序逐个比较 09 if (st.top() != c) ok = false; 10 st.pop(); 11 } 12 cout << (ok ? "YES" : "NO"); 13 return 0; 14}
单选题:程序输出是?
考点:用栈判断回文(M1)。
解析:abcba 全部压栈后弹栈得 abcba(对称串逆序不变)→ YES。正确答案 A。
实现要点:回文判断 = 压栈(得到逆序)+ 弹栈逐个与正序比较。手算:对称串逆序等于自身。
排除法:B 是非回文的输出;C/D 不是输出。
关联 · 栈的数组实现(I 组):STL stack 与数组模拟等价。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 queue<int> q; 05 for (int i = 1; i <= 5; i++) q.push(i); // 5 个人排队 06 while (!q.empty()) { 07 cout << q.front() << " "; // 叫号 08 q.pop(); 09 } 10 return 0; 11}
单选题:程序输出是?
考点:用队列模拟叫号(M2)。
解析:1~5 依次入队、按队头出队 → 1 2 3 4 5(先到先服务)。正确答案 A。
实现要点:叫号模拟 = 入队 + 循环"front 打印 + pop"。手算:跟踪队头变化。
排除法:B 是栈的逆序;C 隔一个出一个;D 是栈顶重复输出。
关联 · 队列与排队系统(E3):概念题的代码版。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int a[5] = {1, 2, 3, 4, 5}; 05 stack<int> st; 06 for (int i = 0; i < 5; i++) st.push(a[i]); 07 while (!st.empty()) { cout << st.top() << " "; st.pop(); } 08 return 0; 09}
单选题:程序输出是?
考点:用栈逆序输出(M3)。
解析:1~5 依次压栈、弹栈输出 → 5 4 3 2 1(后进先出)。正确答案 A。
实现要点:逆序输出 = 全部压栈再弹栈。手算:栈顶总是最后压入的元素。
排除法:B 是原序;C/D 是重复输出的误解。
关联 · 栈的基本概念(A1):LIFO 的直接体现。
单选题:用两个队列模拟栈时,出栈操作的正确做法是?
考点:两个队列模拟栈(M4)。
解析:出栈 = 把前 个元素倒到另一个队列,弹出剩下的最后一个——LIFO 靠"倒腾"实现。正确答案 A。
排除法:B 队列不支持弹队尾;C 队头是最先入队的、不是栈顶;D——两个队列可以模拟栈。
关联 · 栈与队列互相实现(E5):与"两栈实现队列"互为镜像。
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int a[6]; // 循环队列,容量 6,下标 0~5 05 int front = 0, rear = 0; 06 for (int i = 1; i <= 8; i++) { // 依次入队 8 个元素(每 4 次出队 1 次腾位置) 07 a[rear] = i; 08 rear = (rear + 1) % 6; 09 if (i % 4 == 0) front = (front + 1) % 6; 10 } 11 cout << front << " " << rear; 12 return 0; 13}
单选题:程序输出是?
考点:循环队列下标计算(M5)。
解析:入队 8 次(rear 取模走到 2),每 4 次出队 1 次(front 走到 2)→ 2 2。正确答案 A。
实现要点:循环队列 = (rear + 1) % n 入队、(front + 1) % n 出队——取模实现"转圈"。手算:逐个元素跟踪两个指针。
排除法:B 漏了出队;C 前后颠倒;D 只出队一次。
关联 · 循环队列(D 组):取模转圈是循环队列的核心。
用两个栈实现队列:入队压入栈 A;出队时若栈 B 空,把 A 全部倒入 B,再从 B 弹出
判断题:每个元素最多被"倒"一次,均摊复杂度 。
考点:两栈实现队列(M6)。
解析:入队压栈 A;出队时若栈 B 空,把 A 全部倒入 B(倒转顺序),再弹 B——每个元素从 A 到 B 最多一次,均摊 。✅ 正确
实现要点:两栈实现队列 = 入队压栈 A;出队时若栈 B 空,把 A 全部倒入 B(倒转 LIFO 顺序),再从 B 弹出——"倒腾一次"让先入队的到了 B 的栈顶,均摊 。
排除法:无(判断题)。混淆点:单次出队可能 (倒腾时),但均摊 ——"摊还分析"的经典例子。
关联 · 互相实现(E5):两栈倒腾 = 把 LIFO 变 FIFO——"翻转两次抵消"思想。
01const int MAXN = 100; 02int stk[MAXN]; 03int top = -1; // 栈顶下标,-1 表示空栈 04 05// 入栈:把 x 压入栈顶 06void push(int x) { 07 stk[______] = x; 08}
横线处应填( )。
考点:补全 push(N1)。
解析:push 先移栈顶指针再存值:stk[++top] = x——横线填 ++top。✅ A
实现要点:push 的标准写法 stk[++top] = x:"先移指针、再写数据"——前缀 ++ 先加后用;写反成 stk[top++] 会覆盖当前栈顶(P1 的坑)。
排除法:B top++ 先存后移(覆盖栈顶);C top 不移动(覆盖);D top-1 越界。
关联 · push 代码(I2):
++top前缀自增——先加后用。
验算:空栈 top=-1,push x → stk[0]=x, top=0 ✓
01const int MAXN = 100; 02int stk[MAXN]; 03int top = -1; // 栈顶下标,-1 表示空栈 04 05// 出栈:弹出并返回栈顶元素 06int pop() { 07 return stk[______]; 08}
横线处应填( )。
考点:补全 pop(N2)。
解析:pop 先取栈顶再降指针:return stk[top--]——横线填 top--。✅ A
实现要点:pop 的标准写法 return stk[top--]:"先取数据、再降指针"——后缀 -- 先用后减;与 push 的"先移后存"成对,两句口诀记牢。
排除法:B --top 先降再取(取到下面一个);C top 不降(重复取);D top+1 越界。
关联 · pop 代码(I3):
top--后缀自减——先用后减。
验算:栈 [1,2] top=1,pop → stk[1]=2, top=0 ✓
01const int MAXN = 100; 02int q[MAXN]; 03int front = 0, rear = 0; // front 队头、rear 队尾下一个空位 04 05// 入队:把 x 加到队尾 06void push(int x) { 07 q[rear] = x; 08 ______; 09}
横线处应填( )。
考点:补全入队(N3)。
解析:入队 = 写入空位 + 队尾指针后移:q[rear] = x; rear++;——横线填 rear++。✅ A
实现要点:入队 = 写入队尾空位 + 队尾指针后移(q[rear] = x; rear++;)——rear 只管写入,永远不碰 front(P4 的坑)。
排除法:B front++ 动队头(破坏 FIFO);C rear = front 清空;D 是整行替换(语义重复)。
关联 · 入队代码(K2):
q[rear] = x与rear++拆成两行 =q[rear++] = x。
01const int MAXN = 100; 02int q[MAXN]; 03int front = 0, rear = 0; // front 队头、rear 队尾下一个空位 04 05// 出队:取出并返回队头元素 06int pop() { 07 return q[______]; 08}
横线处应填( )。
考点:补全出队(N4)。
解析:出队 = 取队头 + 队头指针后移:return q[front++]——横线填 front++。✅ A
实现要点:出队 = 取队头 + 队头指针后移(return q[front++];)——front 只管读取;误用 rear 就变成"从队尾出"(栈行为,破坏 FIFO)。
排除法:B rear++ 动队尾;C front 不移(重复取);D --rear 从队尾出(栈行为)。
关联 · 出队代码(K3):
q[front++]——取头、指针后移,一步完成。
01const int MAXN = 8; 02int q[MAXN]; 03int front = 0, rear = 0; // 循环队列:指针移动 % MAXN 04 05// 循环队列入队 06void push(int x) { 07 q[rear] = x; 08 rear = ______; 09}
横线处应填( )。
考点:补全循环入队(N5)。
解析:循环队列入队指针要取模:rear = (rear + 1) % MAXN——横线填它。✅ A
实现要点:循环队列入队指针必须取模:rear = (rear + 1) % MAXN——取模 = 越界回绕,是"循环"二字的实现(P3 的坑)。
排除法:B rear + 1 忘取模(越界);C 动 front;D 重置 0(丢循环)。
关联 · 循环入队取模(L2):"凡移动必取模"——循环队列的灵魂(P3 的坑)。
01// 判断括号串是否匹配:左括号入栈,右括号与栈顶配对 02bool check(const string &s) { 03 stack<char> st; 04 for (char c : s) { 05 if (c == '(') st.push(c); 06 else { 07 if (______) return false; // 右括号但栈空 08 st.pop(); 09 } 10 } 11 return st.empty(); 12}
横线处应填( )。
考点:补全括号匹配(N6)。
解析:右括号时栈空 = 无左括号可配 → return false:横线填 st.empty()。✅ A
实现要点:括号匹配的右括号分支:先判空(栈空 = 无左括号可配 → false),再取栈顶配对弹出——空栈检查是第一个失败出口,忘判空会崩。
排除法:B !st.empty() 逻辑反;C st.top() == '(' 是配对检查(本题只有一种括号无需);D 恒真/无关。
关联 · 括号不匹配(B7):右括号遇空栈 = 右括号多余——第一种失败路径。
01// 后缀表达式求值:遇数字入栈,遇运算符弹出两个数计算 02// 弹出时:b = 栈顶(右操作数),a = 次栈顶(左操作数) 03string expr = "53+2*"; // 后缀表达式:5 3 + 2 * 04stack<int> stk; // 数字栈 05int val = 0; 06for (char c : expr) { 07 if (isdigit(c)) stk.push(c - '0'); 08 else { 09 int b = stk.top(); stk.pop(); 10 int a = stk.top(); stk.pop(); 11 if (c == '+') stk.push(______); 12 } 13}
横线处应填( )。
考点:补全后缀求值(N7)。
解析:弹出顺序:b 先弹(右操作数)、a 后弹(左操作数);+ 时压回 a + b(顺序不能反,减/除时尤其关键)。✅ A
实现要点:后缀求值弹栈顺序:先弹的是右操作数(b)、后弹的是左操作数(a)——加法无妨,但减法要写 a - b,写反 b - a 就错。记忆:"先入栈的靠下 = 左边的操作数"。
排除法:B b + a 加法无碍但约定是 a+b(减除会错);C a - b 是减号分支;D 乘号分支。
关联 · 后缀求值(J2):先弹的是右操作数——减/除时
a - b、a / b顺序错则全错。
记忆点:后弹 = 左操作数(先入栈的靠下 = 左边)。
依次执行:push(1) push(2) push(3) pop() push(4) pop()
两次 pop 输出的值是( )。
考点:栈输出序列(O1)。
解析:push1,2,3 → [1,2,3];pop → 3;push4 → [1,2,4];pop → 4。输出 3 4。✅ A
实现要点:栈输出序列题 = 画栈图逐行模拟(I5 的方法);LIFO 决定 pop 顺序 = 入栈顺序的逆序(部分)——"后进先出"在每一步都验证。
排除法:B 1 2 队列序;C 3 2 多弹一次;D 4 3 顺序反。
关联 · 栈模拟(I5):LIFO——后进先出,最近入的先出。
验算:出 3、4 ✓
依次执行:push(1) push(2) pop() push(3) pop() pop()
三次 pop 输出的值是( )。
考点:队列输出序列(O2)。
解析:push1,2 → [1,2];pop → 1;push3 → [2,3];pop pop → 2,3。输出 1 2 3。✅ A
实现要点:队列输出序列题 = 画队形图(K5 的方法);FIFO 决定 pop 顺序 = 入队顺序——与栈题对比着练,两种容器行为差异最清晰。
排除法:B 栈序;C/D 顺序乱。
关联 · 队列模拟(K5):FIFO——先入先出。
验算:出 1,2,3 ✓
01const int MAXN = 4; // 循环队列(浪费一格),front = 0, rear = 0 02// 依次执行:push(1) push(2) push(3) pop() push(4) pop() pop() pop() 03// 依次出队输出的值是( )。
考点:循环队列入出模拟(O3)。
解析:push1,2,3 → 满(3 个,rear=3);pop → 1(front=1);push4 → 写 q[3]、rear 绕回 0;之后 pop 2、3、4。输出 1 2 3 4。✅ A
实现要点:循环队列模拟 = 环形图 + 指针绕圈:rear 绕回后写入的位置是之前 pop 腾出的前端空位——"复用"就在这一绕里。
排除法:B 栈序;C 1 2 4 3 顺序乱;D 漏元素。
关联 · 循环队列(D 组):rear 从 3 绕回 0 写 q[3]——循环复用的完整演示。
验算:出队序 1,2,3,4 ✓
统计括号串中最深的嵌套层数:"(()(()))" 的嵌套深度是( )。
考点:括号匹配计数(O4)。
解析:(()(())) 最大嵌套:( →1 →2,( →3,( →4?——数一下:( 1、( 2、) 1、( 2、( 3、) 2、) 1、) 0 → 最大 3。✅ A
实现要点:嵌套深度 = 栈的最大高度:左括号压栈时高度 +1、右括号弹出时 -1,过程中记录峰值——读括号代码时维护"当前深度 + 最大深度"两个变量。
排除法:B 漏了最内层;C 多数一层;D 只数一层。
关联 · 括号匹配(B1):嵌套深度 = 栈的最大高度——"栈高即深度"。
验算:深度序列 1,2,1,2,3,2,1,0 → 最大 3 ✓
01#include <bits/stdc++.h> 02using namespace std; 03int main() { 04 int n = 13; 05 stack<int> st; 06 while (n > 0) { 07 st.push(n % 2); // 余数入栈 08 n /= 2; 09 } 10 while (!st.empty()) { cout << st.top(); st.pop(); } // 逆序弹出 11 return 0; 12}
单选题:程序输出是?
考点:用栈输出二进制(O5)。
解析: 除 2 取余得 (正序),压栈后逆序弹出 → 1101。正确答案 A。
实现要点:进制转换 = 余数压栈 + 弹栈输出(栈把"倒序读余数"自动化)。手算:、、、。
排除法:B 是余数正序;C 漏了 0;D 是原数。
关联 · 进制转换(第 1 章 B2):除 2 取余、倒序读——栈实现"倒序读"。
01// 入栈: 02void push(int x) { 03 stk[top + 1] = x; // 忘记 top++ 04}
判断题:这样写多次 push 会反复覆盖同一个位置,栈数据丢失。
考点:忘记更新栈顶(P1)。
解析:stk[top + 1] = x 每次都写同一个位置(top 没变),且 top 永远停在初始值——数据互相覆盖。✅ 正确
实现要点:栈的"记忆"全在 top——push 不更新 top 等于没 push(每次覆盖同一位置);对照正确写法 stk[++top] = x(I2),"指针不动的栈是死的"。
排除法:无(判断题)。混淆点:top 不更新 = 栈"看不见"新元素;正确写法 stk[++top] = x。
关联 · push 代码(I2):"先移指针、再写数据"——指针是栈的"记忆"。
01// 出队: 02int pop() { 03 return q[front++]; // 未判断队列是否为空 04}
判断题:对空队列执行该 pop 会取出无意义数据(下溢),应先判空。
考点:出队未判空(P2)。
解析:空队列 q[front++] 会取到无意义数据(下溢),且 front 越界——出队前必须判 front == rear。✅ 正确
实现要点:所有"取"操作前先判空:栈判 top == -1、队列判 front == rear——下溢不报错、悄悄给脏数据,比越界崩溃更隐蔽。
排除法:无(判断题)。混淆点:下溢不报错、悄悄给错数据——比崩溃更危险。
关联 · 队空判断(C4):
if (front == rear) return;(或抛异常)——判空是出队前置。
01// 循环队列出队: 02int pop() { 03 int x = q[front]; 04 front++; // 忘记 % MAXN 05 return x; 06}
判断题:front 会不断增大最终越界,必须写 front = (front + 1) % MAXN。
考点:取模遗漏(P3)。
解析:循环队列 front++ 忘 % MAXN:front 不断增大,最终越界访问——必须 front = (front + 1) % MAXN。✅ 正确
实现要点:循环队列铁律"凡移动必取模"——front/rear 的移动语句里 % MAXN 不可省;忘了就是越界 + 循环失效(P3 专治此病)。
排除法:无(判断题)。混淆点:非循环队列不需要取模;循环队列"凡移动必取模"。
关联 · 循环出队取模(L3):取模 = 越界回绕——循环队列的生命线。
队列操作中写反:
入队时误写 q[front] = x; front++;
出队时误写 return q[rear--];
判断题:这样写会破坏队列结构,队头队尾含义完全颠倒。
考点:front/rear 混淆(P4)。
解析:入队写 front(队头)位置、出队从 rear(队尾)取——方向完全颠倒,队列退化成"反着的栈",FIFO 被破坏。✅ 正确
实现要点:队列铁律"进尾出头"——入队只碰 rear、出队只碰 front;写反就是"反着的栈",FIFO 名存实亡。
排除法:无(判断题)。混淆点:front 只管出、rear 只管进——"进尾出头"是队列铁律。
关联 · 入队出队(C3):front/rear 分工一旦混淆,队列就名存实亡。
下列说法错误的是( )。
考点:综合判断(P5)。
解析:C 错误——循环队列 front == rear 表示队空(浪费一格方案);队满条件是 (rear + 1) % N == front。✅ C
实现要点:综合判断题 = 把每句对照本章细节逐一验证——循环队列"空/满"公式(D2)是最高频错点:front == rear 判空、(rear+1)%N == front 判满。
排除法:A 栈单端操作 ✓;B 队列队头出 ✓;D 栈后进先出 ✓——A/B/D 正确。
关联 · 本章串联:A(A2)、B(C2)、C(D2)、D(E3)——综合题 = 细节判断的集合。