代码随想录算法训练营 Day9 栈与队列 232.用栈实现队列 225. 用队列实现栈 20. 有效的括号 1047. 删除字符串中的所有相邻重复项

栈与队列基础理论

栈和队列是STL(C++标准库)里面的两个数据结构
C++标准库是有多个版本的,要知道我们使用的STL是哪个版本,才能知道对应的栈和队列的实现原理。
那么来介绍一下,三个最为普遍的STL版本:

  1. HP STL: 其他版本的C++ STL,一般是以HP STL为蓝本实现出来的,HP STL是C++ STL的第一个实现版本,而且开放源代码。
  2. P.J.Plauger STL: 由P.J.Plauger参照HP STL实现出来的,被Visual C++编译器所采用,不是开源的。
  3. SGI STL: 由Silicon Graphics Computer Systems公司参照HP STL实现,被Linux的C++编译器GCC所采用,SGI STL是开源软件,源码可读性甚高。

栈(stack)基础理论

栈提供push 和 pop 等等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器(iterator)。 不像是set 或者map 提供迭代器iterator来遍历所有元素。
std::stack<int> myStack={1, 2, 3, 4, 5};,创建栈,栈可以通过已有的容器进行初始化,以vector为容器进行初始化,此时栈中1在栈底,5在栈顶。

std::stack<int> stack(vec);  // 使用 vector 初始化 stack

在这里插入图片描述
栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。STL 中栈是用什么容器实现的?
栈的内部结构,栈的底层实现可以是vector,deque(双端队列),list 都是可以的,主要就是数组和链表的底层实现。
在这里插入图片描述
常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。
栈的基础函数

函数名称函数功能返回值
empty判空bool
size获取栈有多少元素整型
top取栈顶元素返回栈顶元素的引用
push入栈void
emplace入栈(和push有所不同)void
pop移除栈顶元素无返回值
swap交换两个栈的元素void

push和emplace的结果是一样的,都是在栈顶插入一个元素容器获取附加到它的另一个元素。不同之处在于元素的来源。
push 获取一个现有元素,并将其拷贝附加到容器中。简单,直接。 push 总是只接受一个参数,即要复制到容器的元素。
emplace 在容器中创建该类的另一个实例,该实例已经附加到容器中。 emplace 的参数作为参数转发给容器类的构造函数。
pop函数用来返回对栈顶元素的引用,可以访问或者修改栈顶元素。

队列(queue)基础理论

队列是先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。
STL 队列也不被归类为容器,而被归类为container adapter( 容器适配器)。
std::queue<int> myQueue={1, 2, 3, 4, 5};,创建队列,1为队头,5为队尾

 std::queue<int> queue(deq);  // 使用 deque 初始化 queue
函数名称函数功能返回值
empty判空bool
size获取队列有多少元素整型
front返回对队头元素的引用T& 或 const T&
back返回对队尾元素的引用T& 或 const T&
push向队列末尾添加一个元素void
emplace入队列(和push有所不同)void
pop移除队列的第一个元素void
swap交换两个队列的元素void

232.用栈实现队列

使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。

题解思路

利用两个栈来实现队列的行为
在这里插入图片描述

  1. 定义两个栈,一个进栈(stack in ),一个出栈(stack out)
  2. 实现pop函数,先将数据从stack in中全部导出到stack out,
  3. 移除队首元素

代码

class MyQueue {
public:
    stack<int>stackin;
    stack<int>stackout;
    MyQueue() {

    }
    
    void push(int x) {
        stackin.push(x);

    }
    
    int pop() {
        if(stackout.empty())
        {
            while(!stackin.empty())
            {
                stackout.push(stackin.top());   //将stackin的栈头加入到stackout
                stackin.pop();                  //移除stackin的栈顶元素
            }
            
        }
            int result = stackout.top();
            stackout.pop();
            return result;
    }
    
    int peek() {
        if(stackout.empty())
        {
            while(!stackin.empty())
            {
                stackout.push(stackin.top());
                stackin.pop();
            }
            
        }
        return stackout.top();
    }
    
    bool empty() {
        return stackin.empty() && stackout.empty();

    }
};

225. 用队列实现栈

题干

使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空

题解思路

两个队列来模拟栈

队列模拟栈,其实一个队列就够了,队列是先进先出的规则,把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并没有变成先进后出的顺序。第一种方法用两个队列来模拟栈,只不过没有输入和输出的关系,而是另一个队列完全用来备份的,
用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。
在这里插入图片描述

一个队列来模拟栈

栈的pop函数模拟:
思路其实很简单,将队列中后加入的元素弹出再加入到队列中,就可以将最后一个加入队列中的元素弹出,加入队列中有size个元素,那就把size-1个元素弹出再重新加入到队列中,最后就可以将最后一个加入到队列中的元素弹出了。

在这里插入图片描述

class MyStack {
public:
    queue<int>myque;
    MyStack() {

    }
    
    void push(int x) {
        myque.push(x);


    }
    
    int pop() {
        int size = myque.size()-1;
        while(size--)
        {
            myque.push(myque.front());
            myque.pop();
        }
        int result = myque.front();
        myque.pop();
        return result;

    }
    
    int top() {
        return myque.back();

    }
    
    bool empty() {
        return myque.empty();

    }
};

20. 有效的括号

题干

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

题解思路

括号匹配是使用栈解决的经典问题。
题意其实就像我们在写代码的过程中,要求括号的顺序是一样的,有左括号,相应的位置必须要有右括号。如果还记得编译原理的话,编译器在词法分析的过程中处理括号、花括号等这个符号的逻辑,也是使用了栈这种数据结构。
由于栈结构的特殊性,非常适合做对称匹配类的题目。

字符串里的括号不匹配有几种情况

  1. 第一种情况,字符串里左方向的括号多余了 ,所以不匹配。
    在这里插入图片描述

  2. 第二种情况,括号没有多余,但是括号的类型没有匹配上。
    在这里插入图片描述

  3. 第三种情况,字符串里右方向的括号多余了,所以不匹配。
    在这里插入图片描述

在这里插入图片描述

注意点

  1. 字符串的匹配顺序是从左到右,指针先指到的是左符号,当指针指向一个左符号的时候我们在栈里加入一个右符号,当指针指向到右符号的时候我们直接从栈里弹出元素进行匹配就可以了,
  2. 当遍历完字符串的时候发现栈不为空,那就说明出现多余的左符号了
  3. 下一种情况就是当匹配时指针指向的符号和栈中弹出的符号不匹配,这就是第二种情况括号的类型没有匹配上
  4. 最后一种情况就是当指针遍历到右符号进行匹配的时候,发现栈中已经没有符号了,这就是右符号多了
    需要有一个剪枝操作,当这个字符串长度为奇数的时候,那肯定不可能匹配上,

代码实现

  1. 首先进行剪枝判断
  2. 定义栈
  3. 进入for循环,遍历字符串
  4. if遍历到左符号,那么就向栈中添加右符号
  5. else if栈为空,或者栈顶元素与当前遍历的元素不匹配,返回false
  6. 最后一个else,就是栈不为空,且栈顶元素与当前遍历元素匹配的情况,就将栈顶元素弹出
  7. 退出循环之后,再检测一下栈是否为空,如果不为空,那就对应第一种情况,返回false
class Solution {
public:
    bool isValid(string s) {
        if(s.size()%2 != 0)
        {
            return false;
        }
        stack<char>st;
        for(int i=0;i<s.size();i++)
        {
            if(s[i] == '(')
            {
                st.push(')');

            }
            else if(s[i] == '{')
            {
                st.push('}');
            }
            else if(s[i] == '[')
            {
                st.push(']');
            }
            else if(st.empty() || s[i] != st.top()) 
            {
                return false;
            }
            else
            {
                st.pop();
            }
        }
        return st.empty();

    }
};

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

题干

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。
在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
输入:“abbaca”
输出:“ca”
解释:例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

题解思路

本题也是用栈来解决的经典题目,

  1. 新建一个栈
  2. 遍历字符串,进入循环,当遍历到的字符与栈顶元素不相同时或者栈为空时,将此字符导入栈中,否则,删除栈顶元素
  3. 循环结束,新建一个字符串,通过循环将栈中的字符弹出并写入到字符串中
  4. 反转字符串,并返回
class Solution {
public:
    string removeDuplicates(string S) {
        stack<char>st;
        for(char s : S)
        if(st.empty() || s != st.top())
        {
            st.push(s);
        }
        else
        {
            st.pop();
        }
        string result = "";
        while(!st.empty())
        {
            result += st.top();
            st.pop();

        }
        reverse(result.begin(),result.end());
        return result;

    }
};

第二种方法可以拿字符串直接作为栈,这样省去了栈还要转为字符串的操作。
result 是一个标准的 C++ 字符串对象 (std::string),但它被用来模拟栈的行为:

  1. result.back():模拟栈的 top() 操作,用来访问栈顶元素。
  2. result.push_back(s):模拟栈的 push() 操作,将元素 s 压入栈顶。
  3. result.pop_back():模拟栈的 pop() 操作,移除栈顶元素。
  4. result.front():返回字符串的第一个字符的引用(“队列头”元素)。
  5. result.emplace_back():在字符串末尾直接构造字符,效果类似于 push_back(),但更高效,因为它避免了额外的拷贝或移动。
  6. result.clear():移除字符串中的所有字符,将字符串变为空。
    这些函数都是C++标准库的函数,
class Solution {
public:
    string removeDuplicates(string S) {
        string result;
        for(char s : S) {
            if(result.empty() || result.back() != s) {
                result.push_back(s);
            }
            else {
                result.pop_back();
            }
        }
        return result;
    }
};
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值