算法——栈

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

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

题目解析

  • 给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
  • 在 S 上反复执行重复项删除操作,直到无法继续删除。

算法——栈_第1张图片

算法原理

解法——利用栈模拟
这样的过程像小时候的消消乐,有相同的元素就抵消,不同的留下来。并且题目中说了反复执行该删除操作。这个过程就很想数据结构栈里的,和栈顶元素相同就弹出,不同继续入栈。

这里我们并不需要真正用容器中的栈,如果要用容器,还需要将其还原成字符串。并且最终返回结果出栈时是个逆序的形式,我们还需要个reverse才能返回最后结果。所以我们可以使用string来模拟栈。算法——栈_第2张图片

a,b进入string,无特殊情况,当第二个b进入时string里的b比较发现相同,删除string里的b,继续指针向后移
算法——栈_第3张图片
算法——栈_第4张图片

代码实现

class Solution 
{
public:
    string removeDuplicates(string s) 
    {
        string ret; // 搞⼀个数组,模拟栈结构即可
        for(auto ch : s)
        {
            if(ret.size() && ch == ret.back()) ret.pop_back(); // ret里有字符且相等时出栈
            else ret += ch; // ⼊栈
        }
        return ret;
    }
};

比较含退格的字符串

比较含退格的字符串

题目解析

#相当于退格,会把#前面的字符干掉
算法——栈_第5张图片

算法原理

解法:利用栈模拟
遍历a时进栈,遍历b时进栈,当遍历到#时,执行退格操作(弹出栈顶元素)。遍历完毕后先将两个字符串转化成最终形式再进行比较即可。这里还是用字符串模拟栈。算法——栈_第6张图片

代码实现

class Solution 
{
public:
    bool backspaceCompare(string s, string t) 
    {
        return changeStr(s) == changeStr(t);
    }

    string changeStr(string& s)
    {
        string ret; // ⽤数组模拟栈结构
        for(char ch : s)
        {
            if(ch != '#') ret += ch;
            else //当ret里有字符时,执行退格操作
            {
                if(ret.size()) ret.pop_back();
            }
        }
    
    return ret;
    }
};

基本计算器II

基本计算器II

题目解析

  • 整数除法仅保留整数部分
  • **注意:**不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。
  • s 由整数和算符 (‘+’, ‘-’, ‘*’, ‘/’) 组成,中间由一些空格隔开,这里没有括号相对简单
  • 表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内。

算法原理

**解法一 利用双栈模拟计算:**其中一个栈维护数字,另一个栈维护操作符。(在基本计算器I中使用)
**解法一优化:只用一个栈: **因为这里只有加减乘除四则运算,不涉及括号,也没有负数。创建一个int类型的栈来存放数字。再用一个char变量表示运算符。刚开始将char初始化为+,为了接下来操作统一。算法——栈_第7张图片

接下来开始遍历数字(要把它从字符串中提前出来)当遇到第一个数字时,不要提取,先把他放入栈中,因为我们在数字前面初始化了+,我们不知道数字下一个位置的运算符是什么,如果是乘除,这个数字就要和后面的运算符结合。接着继续更新箭头,箭头指向减号,此时char里更新为-。因为栈里存储的未经过运算的数都是+或者-,当最终弹出栈里的数,肯定会执行加法运算,所以可以大胆覆盖更新char存储的运算符号。箭头继续移动到3,观察char中存储的是-,那就将他的相反数放入栈中(此时存储他的相反数-3,保证了最后弹出栈的数字都进行加法运算)。
算法——栈_第8张图片

当箭头移动到4时,此时char里存储的是*,此时把栈顶元素弹出来计算(因为本题中没有括号,乘除法的优先级为最高,所以可以大胆将其弹出进行运算)执行完4*6之后,将24继续放入栈里。除法同理
算法——栈_第9张图片算法——栈_第10张图片

当箭头移动到123,非个位数时,需要将他提取出来,我们此时搞一个tmp,先将其初始化为0,然后遇到一位数将tmp扩大10倍加上去,即移动到1时,010+1=1;移动到2时,110+2=12;移动到3时,12*10+3=123.此时char里存储的是-,将-123存储到栈里。算法——栈_第11张图片

当遍历完之后,将栈里的结果全部进行加法运算即可。
算法——栈_第12张图片
算法——栈_第13张图片

解法二:把中缀表达式转化成后缀表达式(将波兰表达式转化为逆波兰表达式,在基本计算器I中使用)

代码实现

class Solution 
{
public:
    int calculate(string s)
    {
    vector<int> st; // ⽤数组来模拟栈结构
    int i = 0, n = s.size();
    char op = '+';
    while(i < n)
    {
        if(s[i] == ' ') i++;  //空格直接跳过

        //当前字符是数字字符
        else if(s[i] >= '0' && s[i] <= '9')
        {
         // 先把这个数字给提取出来
            int tmp = 0;
            while(i < n && s[i] >= '0' && s[i] <= '9') //遇到非个位数提取
            tmp = tmp * 10 + (s[i++] - '0');
            
            //+ - 直接入栈,* / 与栈顶元素计算
            if(op == '+') st.push_back(tmp);
            else if(op == '-') st.push_back(-tmp);
            else if(op == '*') st.back() *= tmp;
            else st.back() /= tmp;
        }
            
        //当前箭头所指字符是操作符    
        else 
        {
        op = s[i];  //更新op
        i++;
        }
    }
    int ret = 0;
    for(auto x : st) ret += x;
    return ret;
    }
};

字符串解码

字符串解码

题目解析

方框嵌套需要由内到外。

算法——栈_第14张图片

算法原理

方框嵌套与运算中括号相似,都是需要由内向外计算,这里还是需要借助栈。从头开始遍历,遇到1、[ 、a、2、[ 、b、c都先存入栈中。当遇到 ] 时,把3和c拿出来进行解码,因为此时会有一个左方框与之匹配。因为我们解码是拿最近存储的进行解码,符合栈后进先出的规则。我们可以定义两个栈结构,⼀个⽤来保存解码前的重复次数 k (左括号前的数字),⼀个⽤来保存解码之前字符串的信息(左括号前的字符串信息)

刚开始遇到数字,要先把数字提取出来,放到int栈里,碰到左括号,将左括号后的字符提取出来放入string栈里。然后碰到右括号,将两个栈栈顶元素拿出来。因为右括号要去匹配最近的左括号。
算法——栈_第15张图片

当2和bc解码之后变为bcbc,但此时不能直接放入栈中,因为不确定bcbc前面是否连着其他字符,所以要把bcbc放入栈顶元素后面。之后继续向后移动发现第二个右括号,此时将1和abcbc拿出来解码,但是此时栈顶元素为空,就不能直接将他放入栈顶元素后面,否则会造成越界访问的风险,我们可以在最开始时在栈里放入一个空字符串。

继续向后访问,当碰到一个单独的字符串后,将两个字符串直接解析出来,直接放在栈顶元素字符串的后面。
算法——栈_第16张图片

算法——栈_第17张图片

代码实现

class Solution 
{
public:
    string decodeString(string s) 
    {
    stack<int> nums;
    stack<string> st;
    st.push("");
    int i = 0, n = s.size();
    while(i < n)
        {
            if(s[i] >= '0' && s[i] <= '9')
            {
                int tmp = 0;
                while(s[i] >= '0' && s[i] <= '9')
                {
                    tmp = tmp * 10 + (s[i] - '0');
                    i++;
                }
                nums.push(tmp);
            }

            else if(s[i] == '[')
            {
                i++; // 把括号后⾯的字符串提取出来
                string tmp = "";
                while(s[i] >= 'a' && s[i] <= 'z')
                {
                    tmp += s[i];
                    i++;
                }
                st.push(tmp);
            }
        
            else if(s[i] == ']')
            {
            string tmp = st.top();
            st.pop();
            int k = nums.top();
            nums.pop();
                while(k--)
                {
                    st.top() += tmp;
                }

               i++; // 跳过这个右括号
            }
            
            else
            {
                string tmp;
                while(i < n && s[i] >= 'a' && s[i] <= 'z')
                {
                    tmp += s[i];
                    i++;
                }

                st.top() += tmp;
            }
        }
        return st.top();
    }
};

验证栈序列

验证栈序列

题目解析

每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false 。
算法——栈_第18张图片

算法原理

借助栈:让push一直进栈,进栈的同时判断push是否出栈。在push出栈这里定义一个指针i,指向第一个元素用来标记一下接下来谁要先出栈。让1进栈,此时判断pop中指针指向的将要出栈的元素,1不出继续进栈,2、3、4都接着进栈。此时4要出栈,出栈之后让pop里的指针后移继续指向将要出栈的元素,i指向5,继续判断栈顶元素是否为出栈元素。(此时栈里有123),不是将要出栈的元素就继续进栈。1235.
算法——栈_第19张图片

当发现栈顶元素和将要出栈的元素不一样时,此时看指针是否遍历到最后一个位置,如果不是,就说明这个序列是错误的
算法——栈_第20张图片
当这个栈是合法的时,此时i会移动到最后一个位置,栈为空栈
算法——栈_第21张图片

代码实现

class Solution 
{
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) 
    {
        stack<int> st;
        int i = 0, n = popped.size();
        for(auto x : pushed)
        {
            st.push(x);
            while(st.size() && st.top() == popped[i])   
            {
                st.pop();
                i++;
            }
       }
    return i == n;
    }
};

你可能感兴趣的:(算法,算法)