栈类型题目

2026-09-12

引言

栈类型的题目应该是面试中比较常见的,毕竟思路对了基本马上可以做出来。关键其实只是对于堆的思路的理解。此处在总结栈类型的题目的时候也把队列也一并介绍

  • 栈(stack)是先入后出的
  • 队列(queue)是先入先出的
  • 双端队列(dueue)则是两端都可了~

更直观的请见下图

下图更加直观的展示了栈和队列的区别

下面则详细的列举了定义及函数的使用:

有效括号

class Solution {
  public:
      bool isValid(string s) {
          stack<char> group;
          for(int i=0;i<s.size();i++)
          {
              if(s[i]=='(' || s[i]=='[' || s[i]=='{')
                  group.push(s[i]);
              else
              {
                  if(!group.empty())//若不为空
                  {
                      if(
                          (s[i]==')' && group.top()=='(')
                          ||(s[i]==']' && group.top()=='[')
                          ||(s[i]=='}' && group.top()=='{')
                        )
                          group.pop();//找了就删掉
                      else //找不到匹配的就不是
                          return false;
                  }
                  else
                      return false;
              }
          }
          if(group.size()) //结束后如果不是空的,
              return false;
          else
              return true;
      }
  };

最长有效括号

保持栈底元素为当前已经遍历过的元素中「最后一个没有被匹配的右括号的下标」这样才是连续成对的。空间复杂度和时间复杂度都为O(N)

class Solution {
  public:
      int longestValidParentheses(string s) {//输入为只包含 '(' 和 ')' 的字符串
  
          int max_len=0;//最长有效(格式正确且连续)
          stack<int> stack_index;//记录index索引的
  
          stack_index.push(-1);//初始化先push一个-1的索引。作为「最后一个没有被匹配的右括号的下标」
  
          //左右括号成一对,记录的是当前的右括号与上一个不成对的右括号
          for(int i=0;i<s.size();i++)
          {
              if(s[i]==')')//若是右括号
              {
                  stack_index.pop();//删掉这个右括号前一个的左括号(弹出一个栈顶表示匹配了当前的括号!)
                  if(!stack_index.empty())//若不为空
                  {
                      int range=i-stack_index.top();
                      max_len=max(max_len,range);//记录连续的~
                  }
                  else//若为空说明:当前的右括号为没有被匹配的左括号,我们将其下标放入栈中来更新我们之前提到的「最后一个没有被匹配的右括号的下标」
                      stack_index.push(i);//存入,这个右括号的索引。为当前最后一个没有被匹配的右括号(下次会先删除)
              }
              else
                  stack_index.push(i);//存入左括号的索引
          }
          return max_len;
      }
  };

移掉K位数字

需要先解读题目,数字应该是顺序不变的。那要保证尽量递增的形式.且要避免前导为0(也就是栈为空时,0不要放入)

class Solution {
  public:
      string removeKdigits(string num, int k) {
          stack<char> stack_char;
          for(int i=0;i<num.size();i++)
          {
              //每次先检查,如果满足可以先从栈中删除一些:满足条件:从左到右为递增(小的放前面)
              while(!stack_char.empty() && k>0 && stack_char.top()>num[i])
              {
                  // 当盏不为空,且盏顶的元素大于当前的元素的时候,把栈顶的元素去掉。放入当前的元素
                  stack_char.pop();
                  k--;//计数值自减
              }
              if(stack_char.empty() && num[i]=='0')//避免了前导为0
                  continue;//跳过第一个为0
              stack_char.push(num[i]);//每次都将当前值放入
          }
  
          string result;//记录结果
          while(!stack_char.empty())
          {
              if(k>0)//如果还需要继续删除位数
                  k--;
              else //只有当不需要去除数字的时候才加入
                  result+=stack_char.top();//注意这个比下面的空间复杂度更低
                  // result=stack_char.top()+result; //每次赋值导致更高的内存消耗
              stack_char.pop();//弹出栈顶
          }
          //栈是反过来的因此需要反转一下
          reverse(result.begin(), result.end());//stl中的reverse函数,需要反转一下。因为栈是逆过来的
          return result.empty()? "0":result;//注意最后结果是否为空
  
      }
  };

string代替栈:去掉重复字母

class Solution {
  public:
      string removeDuplicateLetters(string s) {       
         //先通过unordered_map记录每个字符出现了多少次
         unordered_map<char,int> map;
         for(auto str:s)
              map[str]++;
  
          stack<char> stack_str;//定义一个栈
          unordered_map<char,int> map_stack;//用于记录这个字符是否在栈中
          for(int i=0;i<s.size();i++)
          {
              if(map_stack[s[i]]>0)//当前已经存在于stack中了
              {
                  map[s[i]]--;
                  continue;
              }
              //否则就是不存在了~
              while(!stack_str.empty() && stack_str.top()>s[i] && map[stack_str.top()]>0)
              {//当前盏不为空,且top的元素大于s[i],且stack_str.top()元素数量大于0,也就是后面还有
                  map_stack[stack_str.top()]=0;//记录(不在栈中)
                  stack_str.pop();//出栈
              }
              stack_str.push(s[i]);//入栈
              map_stack[s[i]]=1;//入栈记录
              map[s[i]]--;//也要记录
          }
  
         string output_str;
         while(!stack_str.empty())
         {
              output_str=stack_str.top()+output_str;//注意顺序就不需要采用reverse
              stack_str.pop();
         }
         return output_str;
      }
  };

上面方法虽然通过栈可以解决,但是却很容易出错,还需要额外记录。因此下面给出用string的解法。其实也是用了栈的思想,只是用了string可以用其函数find这样可以简化操作

class Solution {
  public:
      string removeDuplicateLetters(string s) {
          // 注意要求是字典序最小其不能打乱字符,单纯的unordered_map解决不了~
          // 对于输入bcabc。第一个是b放入,第二个发现是c,b<c,也将c放入
          // 当遇到a,少于c,且a后面有c,所以c删掉。小于b,所以b删掉。
         
         //先通过unordered_map记录每个字符出现了多少次
         unordered_map<char,int> map;
         for(auto str:s)
         {
              map[str]++;
         }
  
         string output_str;
         for(int i=0;i<s.size();i++)
         {    
              //说明当前字符已经存在了,故此不再插入
              if(output_str.find(s[i])!=string::npos)
              {
                  map[s[i]]--;//记录这个字符的数目减一,虽然没有入栈,但是这个字符也被剔除掉了
                  continue;
              }
  
              //如果没有找到
              //且栈内不为空,且即将进栈的元素小于当前栈顶的元素,同时当前栈顶的元素也不是最后一个(map中还有)
              while(!output_str.empty() && output_str.back()>s[i] && map[output_str.back()]>0)
              {
                //循环对比
                  output_str.pop_back();//就可以删掉(相当于出栈)
                  //注意由于原本这个字符入栈的时候已经减一了,所以这里不需要再减一
              }
              output_str.push_back(s[i]);//入栈操作
              map[s[i]]--;//入栈后减一。
         }
         return output_str;
      }
  };

简化路径

时间与空间复杂度都是O(N)

class Solution {
  public:
      string simplifyPath(string path) {
  
          // if(path.back()!='/')
          //     path=path+'/';//在结尾处加上'/'方便对路径的字母进行分组放于str_group中

          vector<string> str_group;
          string temp_str;
          for(int i=0;i<path.size();i++)//进行路径的分割
          {
              if(path[i]=='/') //遇到斜杆
              {
                  if(!temp_str.empty())//如果不为空就push到vector中
                  {
                      str_group.push_back(temp_str);
                      temp_str.clear();//清空一下
                  }
              }
              else
                  temp_str=temp_str+path[i];
          }//这样就实现了以斜杆划分,并且不包含斜杆
          //存入最后一个
          if(!temp_str.empty())
              str_group.push_back(temp_str);//若不为空,存进去(不要漏掉了!!!)
  
          stack<string> path_stack;//定义栈
          for(int i=0;i<str_group.size();i++)//遍历分割后的路径
          {
              string temp_str=str_group[i];;
              // if(!temp_str.empty())//作为double check
              // {
                  if(temp_str==".")//若为一个点,不处理
                  {}
                  else if (temp_str=="..")//如果连续两个点,那么就是要切换到上一级目录
                  {
                      if(!path_stack.empty())//如果栈不为空,就把栈顶删掉
                          path_stack.pop();
                  }
                  else//上面都不满足,就应该是正常字符,所以输入作为路径
                      path_stack.push(temp_str); //放入栈中
              // }
          }
  
          //最终结果存放到string中
          string output_str;
          while(!path_stack.empty())
          {
              output_str = "/" + path_stack.top() + output_str;//注意顺序,加到当前的前面
              path_stack.pop();
          }
          return output_str.empty()? "/":output_str;//若为空那么只有一个斜杆
      }
  };

矩阵乘法计算量估算

时间复杂度和空间复杂度都为O(N)关键点是:1、每对括号包含两个矩阵。2、矩阵的乘法数量应该是matrix_1.second*matrix_2.second*matrix_1.first也就是三个不一样的数字相乘啦

#include <iostream>
#include <utility>
#include <vector>
#include <stack>
using namespace std;

int main() {
    int num;
    cin>>num;
    vector<pair<int,int>>matrix_group;
    for(int i=0;i<num;i++)
    {
        int a,b;
        cin>>a>>b;
        auto each=make_pair(a, b);
        matrix_group.push_back(each);
    }
    string input_str;
    cin>>input_str;

    stack<pair<int,int>> string_stack;//定义一个栈来存行列
    int result=0;
    for(int i=0,j=0;i<input_str.size();i++)
    {
        if(input_str[i]=='(')//找到一个左括号,不做任何操作
        {
            int do_nothing;
        }
        else if(input_str[i]==')')//找到一个右括号,先出栈2个,必然为2个!!!!!
        {
            if(!string_stack.empty())
            {
                auto matrix_2=string_stack.top();
                string_stack.pop();//取出后删掉
                auto matrix_1=string_stack.top();
                string_stack.pop();//取出后删掉
                //注意矩阵乘法的数目,
                result+=matrix_1.second*matrix_2.second*matrix_1.first;//matrix_1.second=matrix_2.first
                auto each_new=make_pair(matrix_1.first, matrix_2.second);
                string_stack.push(each_new);
            }
        }
        else {//入栈
            string_stack.push(matrix_group[j]);
            j++;
        }
    }
    std::cout<<result;
}

参考资料