【优选算法】栈

目录

一、删除字符串中的所有相邻重复项

题目描述:

思路讲解:

本题的目的很简单,看见两个相邻且相同的字符时,将这两个字符从字符串中删除,删除这两个字符之后可能会出现前后两个字符变为相邻相同的情况,所以需要重复进行删除的操作。

本题可以使用栈来解决,定义一个变量i来遍历字符串,对下面不同情况分类讨论:

  1. 当栈中没有元素或是栈顶元素与下标i对应的元素不同时,将下标i对应的元素放入栈中
  2. 当栈顶元素与下标i对应的元素不同时,将栈顶元素删除,并将i向右移动,但是新的栈顶元素和新下标i对应元素可能也会相同,如果多次满足这个情况则需要重复这个操作

当完成上面的操作后,再将栈中的元素取出组成字符串,但是取出来的字符串是逆序的,我们需要逆转后再返回,所以本题可以使用数组来模拟栈,取出来组成的字符串就可以直接返回。

编写代码:

cpp 复制代码
class Solution {
public:
    string removeDuplicates(string s) {
        string ans;
        int sLen = s.size();
        int ansi = -1;
        for(int i = 0 ; i < sLen ; i++)
        {
            // 遇到相邻或消除相邻后字母相邻的消除
            while(ansi >= 0 && ans[ansi] == s[i])
            {
                ans.pop_back();
                ansi--;
                i++;
            }

            if(i < sLen)
                ans += s[i];
            ansi++;
        }

        return ans;
    }
};

二、比较含退格的字符串

题目描述:

思路讲解:

本题与上一题的思路基本一致,上一题是前面一个字符和后面一个字符相同时,删除这两个字符,本题是后面一个字符是'#'就删除前面一个字符和'#'。

使用数组来模拟栈,定义一个变量i来遍历字符串,对下面不同情况分类讨论:

  1. 当下标i对应的字符不为'#'时,将i位置上的字符添加到栈中
  2. 当下标i对应的字符为'#'时,删除栈顶元素并且i向右移动

使用这个方法对两个字符串进行处理,最终会得到两个数组,将数组中的字符处理成两个字符串进行比较,相同返回true,不相同返回false。

编写代码:

cpp 复制代码
class Solution {
public:
    bool backspaceCompare(string s, string t) {
        string tmp1 , tmp2;
        for(auto ch : s)
        {
            if(ch != '#')
                tmp1 += ch;
            else if(tmp1.size() > 0)
                tmp1.pop_back();
        }

        for(auto ch : t)
        {
            if(ch != '#')
                tmp2 += ch;
            else if(tmp2.size() > 0)
                tmp2.pop_back();
        }

        if(tmp1 == tmp2)    return true;
        else return false;
    }
};

三、基本计算器 II

题目描述:

思路讲解:

本题只有'+', '-', '*', '/'这四个运算符,所以相对来说还是比较简单的,当我们遍历到数组中的'+''-'时,由于不知道后面一个数的运算符是什么,所以不能将前后的两个数字进行直接运算,那么我们就需要选择一个容器将数字存起来,这里我们选择栈来存储数字。

这里我们定义一个栈nums来存储数字,定义一个char op来记录某一个整数前的运算符,由于本题中出现的所有整数都是正整数,就建op设置为+,再定义一个变量i来遍历数组,遍历数组时,我们将出现的情况分为四种:

  1. 空格,当i指向的元素为空格时,仅需i向右移即可
  2. 数字,当i指向的元素为数字时,由于数字是由字符串形式存在的,我们需要将它提取出来,定义一个变量sum,当连续出现字母时,i每指向数字时,就先将sum*10,再将sum加上i指向的数字,最后i向右移动
  3. 运算符'+''-',当运算符为这两个时,将op修改为当前的运算符,这时我们并不能将运算符前后的两个数字进行运算,所以先将刚刚提取出来的数字带上运算符的属性添加到栈中,最后i向右移动
  4. 运算符'*''/',将op修改为当前的运算符,这两个运算符在本题中的优先级最高,遇到后只需要将该运算符前后两个数字进行运算即可,也就是取出栈顶元素和提取后面的数字再进行运算,最后将运算的结果放入到栈中,最后i向右移动

由于我们将数字加入到栈中时,都是带来+或-的,做完上面的操作后,我们就将-、/、*这三个运算符处理完了,最后只需要将栈中所有的数字相加即可得到本题的答案。

编写代码:

cpp 复制代码
class Solution {
public:
    int calculate(string s) {
        char op = '+';     // 记录某个整数前面的运算符
        stack<long long> nums;   // 栈中存储数字
        int sLen = s.size();
        for(int i = 0 ; i < sLen ;)
        {
            // s[i] 是空格时
            if(s[i] == ' ')    i++;

            // s[i] 为运算符时
            if(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
                op = s[i++];

            // s[i] 是空格时
            if(s[i] == ' ')    i++;

            long long tmp = 0;
            while('0' <= s[i] && s[i] <= '9')
            {   
                tmp = tmp * 10 + s[i++] - '0';
            }

            // s[i] 是空格时
            if(s[i] == ' ')    i++;

            if(op == '+')
                nums.push(tmp);
            else if(op == '-')
                nums.push(-tmp);
            else if(op == '*')
                nums.top() *= tmp;
            else if(op == '/')// op == '/'
                nums.top() /= tmp;
        }

        int ans = 0;
        while(!nums.empty())
        {
            ans += nums.top();
            nums.pop();
        }

        return ans;
    }
};

四、字符串解码

题目描述:

思路讲解:

当我们遍历到'['时,需要将[]中的字符串变为原来的k倍,我们并不知道[]中的字符串中是否还存在'[]',所以就需要将[之前的数字和[之后的字符串分别存储起来,这里我们选择定义两个栈来分别存储数字和字符串。

定义一个字符串栈stStr和一个数字栈stNum,再定义一个变量i来遍历数组,遍历数组时,我们将出现的情况分为四种:

  1. 遇到数字,将这个数字放入到数字栈中
  2. 遇到'[',把后面的字符串提取出来放到字符串栈中
  3. 遇到']',将字符串栈栈顶元素和数字栈栈顶元素提取出来,按照题目中的方式进行处理后,再放入到字符串栈中
  4. 遇到单独的字符,提取出这个字符串,将这个字符串放入到字符串栈栈顶的字符串的后面

把上面的操作做完后,字符串栈中仅剩的字符串就是本题的答案,取出来并返回即可解决本题。

本题由于个小细节就是需要在字符串栈中先添加一个空字符串,这样能够保证对两个栈的处理过程相同。

编写代码:

cpp 复制代码
class Solution {
public:
    string decodeString(string s) {
        stack<string> stStr;
        stack<int> stNum;
        int flag = 0;    // 标记前面是否出现过[
            
        // 这里字符串栈先入一个空字符串
        // 使后面字符串接到字符串栈栈顶的操作保持一致
        // 不需要再判断是否为空栈的情况
        stStr.push("");

        int sLen = s.size();
        for(int i = 0 ; i < sLen ; )
        {
            int tmpNum = 0;
            // 数字后面必定是[
            while('0' <= s[i] && s[i] <= '9')
            {
                tmpNum = tmpNum * 10 + s[i++] - '0';
            }
            // 这里可能没有数字
            if(tmpNum)
                stNum.push(tmpNum);
            
            // 当s[i] 为 [ 时将flag标记为1
            if(s[i] == '[')
            {
                flag = 1;
                i++;
            }
            
            // 提取连续的字符串
            string tmp = "";
            while('a' <= s[i] && s[i] <= 'z')
            {
                tmp += s[i++];
            }

            // 遇到[就将后面的字符串入字符串栈
            // 没遇到[就将后面出现的字符串接到字符串栈栈顶元素的后面
            if(flag == 1)
                stStr.push(tmp);
            else
                stStr.top() += tmp; 

            // 遇到]则取出数字栈栈顶的数字(n个),和字符串栈栈顶的字符串(s)
            // 变为 n 个 s相连接,再接到字符串栈栈顶元素的后面
            if(s[i] == ']')
            {
                int num = stNum.top();
                stNum.pop();

                string str;
                while(num--)
                    str += stStr.top();
                
                stStr.pop();

                stStr.top() += str;

                // 不能再判断条件哪里i++
                // 这里可能是数字
                i++;
            }

            // 标记变回0
            flag = 0;
        }
        return stStr.top();
    }
};

五、验证栈序列

题目描述:

思路讲解:

本题只需要定义一个栈,然后对push和pop进行模拟即可完成本题。

定义一个栈st,定义两个变量pushi和popi分别用来遍历pushed数组和poped数组,首先将pushi指向的元素放入到栈中,再使栈顶元素与popi指向的元素进行对比,会出现两种情况:

  1. 栈顶元素与popi指向的元素不同,pushi向右移动
  2. 栈顶元素与popi指向的元素相同,删除栈顶元素,popi向后移动,重复与栈顶元素进行对比,直到不相同或是栈为空

当上面的操作做完以后有两种方式判断推入 push 和弹出 pop 操作序列是否为正确的序列,一是栈为空则是正确的序列,二是popi遍历到poped数组的结尾是正确的序列,选择其中的一个方式即可。

编写代码:

cpp 复制代码
class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> st;
        int pushLen = pushed.size();
        int popLen = popped.size();

        int pushi = 0 , popi = 0;
        while(pushi < pushLen)
        {
            if(st.empty() || st.top() != popped[popi])
                st.push(pushed[pushi++]);

            while(!st.empty() && st.top() == popped[popi])
            {
                st.pop();
                popi++;
            }
        }

        if(st.empty())
            return true;
        else
            return false;
    }
};

结尾

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。

希望大家以后也能和我一起进步!!🌹🌹

如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

相关推荐
dundunmm3 分钟前
机器学习之KNN算法
人工智能·算法·机器学习·数据挖掘·knn·分类算法
蓝胖子教编程10 分钟前
【题解】【枚举】——[NOIP2018 普及组] 龙虎斗
c++·算法
冷眼看人间恩怨23 分钟前
【Qt笔记】QComboBox控件详解
c++·笔记·qt
奇偶变不变41 分钟前
RTOS之事件集
java·linux·jvm·单片机·算法
Rossy Yan44 分钟前
【C++面向对象——类的多态性与虚函数】计算图像面积(头歌实践教学平台习题)【合集】
开发语言·c++·多态·面向对象·虚函数·头歌实践教学平台
Sunsets_Red44 分钟前
CF1548A Web of Lies 题解
c++·学习·算法·信息与通信
Solitudefire1 小时前
蓝桥杯刷题——day7
算法·蓝桥杯
人才程序员1 小时前
【无标题】
c语言·前端·c++·qt·软件工程·qml·界面
bae-唯一1 小时前
扫雷游戏(基础版)
c++·游戏
了一li2 小时前
MATLAB转换C语言--问题(一)FFT 和 IFFT 的缩放因子
算法