代码随想录笔记|C++数据结构与算法学习笔记-栈和队列(一)|232.用栈实现队列、225. 用队列实现栈、20. 有效的括号、1047. 删除字符串中的所有相邻重复项、150. 逆波兰表达式求值

232.用栈实现队列

力扣题目链接

整体思路

此题比较简单,栈是先进后出,队列是先进先出。想要用栈来实现队列,明显就是直接用两个栈,来实现数据的出栈顺序。

把栈A的数据出栈,压入到栈B中。然后数据再从栈B出栈。就用栈实现了先进先出。

int pop()

  • 首先一定要保证stackOut为空,不然的话顺序就乱掉了

int peek()

  • 直接使用已有的pop函数,直接复用pop(), 要不然,对stOut判空的逻辑又要重写一遍。

再多说一些代码开发上的习惯问题,在工业级别代码开发中,最忌讳的就是 实现一个类似的函数,直接把代码粘过来改一改就完事了。

  • 如果使用pop函数弹出了元素res,一定要添加再添加回去

伪代码

stackIn stackOut;

//入栈
void push (int x){
  stackIn.push(x);
}

//出栈
int pop()
{
  if (stackOut.empty())
    while (!stackIn.empty())
    { stackOut.push(stackIn.top);
  		stackIn.pop();
    }
  result = stackOut.top();
  stackOut.pop;
  return result
}
 
//获取队列出口处的第一个元素,这个我们还是要从out栈里边获取这个元素
int peek()
{
	result = this->pop();
	stackOut.push(result);//为什么我们要pop出来然后又push进去,因为我们就只是查询一下而已
  return result;
}

C++ 代码

class MyQueue {
public:
    stack<int> stIn;
    stack<int> stOut;
    /** Initialize your data structure here. */
    MyQueue() {

    }
    /** Push element x to the back of queue. */
    void push(int x) {
        stIn.push(x);
    }

    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        // 只有当stOut为空的时候,再从stIn里导入数据(导入stIn全部数据)
        if (stOut.empty()) {
            // 从stIn导入数据直到stIn为空
            while(!stIn.empty()) {
                stOut.push(stIn.top());
                stIn.pop();
            }
        }
        int result = stOut.top();
        stOut.pop();
        return result;
    }

    /** Get the front element. */
    int peek() {
        int res = this->pop(); // 直接使用已有的pop函数
        stOut.push(res); // 因为pop函数弹出了元素res,所以再添加回去
        return res;
    }

    /** Returns whether the queue is empty. */
    bool empty() {
        return stIn.empty() && stOut.empty();
    }
};

225. 用队列实现栈

力扣题目链接

用一个队列来模拟栈的进元素和出元素

整体思路

设队列A,队列从出口到入口的顺序[1, 2, 3]。

栈B,从栈底到栈顶是[1, 2, 3]

关键在于pop到时候,栈要先弹出元素3,那么现在我们只有一个队列,如何先弹出3呢?

其实我们只需要把元素1取出来加入队列的队尾,把元素2取出来加入队列的队尾,这样元素3就到队列的出口了,我们就可以先弹出元素3。更加一般得来说,如果队列长度是size,我们把size - 1个元素弹出即可。

伪代码

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

void pop ()
{
  //获取队列的size,因为上文介绍过了,我们需要弹出size - 1个元素
  size = que.size();
  size--;	//此时就是size-1了
  while(size--)
  {
    que.push(que.front())		//就是把我们的前部元素直接加回到队列里面front只是取了第一个元素,没有做弹出
    que.pop();							//这里就把前部元素弹出了
  }
  result = que.front();
  que.pop;
  return result;
}

//获取栈出口处的第一个元素,而不用弹出
void top()
{
  return que.back()
}

C++代码

class MyStack {
public:
    queue<int> que1;
    queue<int> que2; // 辅助队列,用来备份
    /** Initialize your data structure here. */
    MyStack() {

    }

    /** Push element x onto stack. */
    void push(int x) {
        que1.push(x);
    }

    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int size = que1.size();
        size--;
        while (size--) { // 将que1 导入que2,但要留下最后一个元素
            que2.push(que1.front());
            que1.pop();
        }

        int result = que1.front(); // 留下的最后一个元素就是要返回的值
        que1.pop();
        que1 = que2;            // 再将que2赋值给que1
        while (!que2.empty()) { // 清空que2
            que2.pop();
        }
        return result;
    }

    /** Get the top element. */
    int top() {
        return que1.back();
    }

    /** Returns whether the stack is empty. */
    bool empty() {
        return que1.empty();
    }
};

20. 有效的括号

力扣题目链接

文字链接:20. 有效的括号

视频链接:栈的拿手好戏!| LeetCode:20. 有效的括号

题目分析

由于栈结构的特殊性,非常适合做对称匹配类的题目

首先我们需要理解三种括号不匹配的情况

  • 第一种情况,字符串里左方向的括号多余,所以不匹配

    ( [ { } ] ( ),其中最左边多余一个 (

  • 第二种情况,括号没有多余,但是括号的类型没有匹配上

​ ( [ { } } },其中方括号 [ 匹配错误,圆括号 ( 也匹配错误

  • 第三种情况,字符串里右方向的括号多余了,所以不匹配

​ ( [ { } ] ) ) ),其中右方向的括号多余,不匹配

那么次题中,栈用来干什么呢

我们从第一个符号开始遍历,每遍历一次,我们把它的右括号入栈,然后等到匹配的时候,对应出栈即可。非常简单方便。


综上:

第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false

第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false

第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false


字符串遍历完之后,栈是空的,就说明全都匹配了。

再强调一个重要的点就是,如果我们遇到左括号,应该把右括号入栈,到时候出栈的时候有利于我们的代码实现(这样很方便匹配)。等到只要有右括号,我们直接拿这个右括号和我们的栈顶匹配,匹配上了,把该右括号出栈

剪枝

如果是找匹配的括号,那么这个字符串肯定是个偶数。

伪代码

stack<char> st;

if (s.size % 2 != 0) return false;
for (i = 0; i < s.size; i++)
{
  //遇到左括号的场景
  if (s[1] == '(')	st.push(')');
  else if (s[i] == '{') st.push('}');
  else if (s[i] == '[') st.push(']');
  
  //遇到右括号的场景,此时我们要到栈内去匹配
  //第三种情况,匹配的时候栈已经空了,没有匹配的字符了st.empty()
  //第二种情况,遍历字符串匹配的过程中,发现栈里不是我们要匹配的字符。st.top()!=s[i]
  //一定要注意,先检查空栈的情况,再去看匹配不上的情况,不能写成st.top() != s[i]||st.empty()
  else if (st.empty() || st.top() != s[i]) return false;
  else st.pop(); //st.top() 与 s[i]相等,栈弹出元素
}
return st.empty();

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

力扣题目链接

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

视频链接:LeetCode:1047. 删除字符串中的所有相邻重复项

次题如果不没想到要用栈和队列的思想。过程就相当复杂

不仅要找相邻的重复项,还要在相邻的重复项删除了之后如果又有相邻处,还要继续删除。

比如对于字符串"abbaca"删除重复项之后变成"aaca"还要再次删除"aa"。这样就相当麻烦。

  • 所以次题为什么适合用栈来解决呢?

两个一样的字符相邻就删除,像一种消消乐的规则,这类问题用栈也非常合适,其实也像一个匹配题

  • 那么次题中,栈用来干什么呢?

用来存我们遍历过的元素,我们每遍历一个元素,都去栈里面询问一下,我之前有没有遍历a,我的前一个字母是a吗。其实就是通过栈这个数据结构来获得我们前一个遍历的元素是什么。

如果前后元素不一样,我们就继续往后遍历,并入栈,直到碰到相邻重复元素。

例如对于字符串“abbac”。我们现在遍历到第二个b,然后栈顶元素也是b,那么就消除,一下个遍历a,但是由于栈顶的b被消除了,此时的栈顶是a,然后我们再次完成消除,最后遍历c、a。

这样我们就完美解决了之前说的在相邻的重复想删除了之后又有相邻项,完成继续删除的操作

  • 完成删除重复项之后,我们栈内的元素和我们的目标字符串不是反的么?(画个图 比较好理解这句话的意思)

其实我们可以用字符串来模拟栈,然后把出口(栈顶)作为字符串的尾巴,把栈底作为字符串的头部。这样这个字符串不就是目标字符串了么。也就是对于字符串“abbac”。我们在字符串模拟的栈里面的结果是

尾 [ a c ] 头 尾[a c]头 [ac]

伪代码

string resultStack;
for (char s : S)
{
  //我们遍历的时候首先就和栈内元素做比较,如果这个栈本来就是空的或者该字符不等于字符串的尾部
  if (stack.empty() || s != resutStack.push_back())	
    //此时我把元素放入字符串的尾部
    result.push_back(s);
  else	resultStack.pop_back();	//从尾部弹出
}
return resultStack

150. 逆波兰表达式求值

力扣题目链接

文字链接:150. 逆波兰表达式求值

视频链接:栈的最后表演! | LeetCode:150. 逆波兰表达式求值

逆波兰表达式即后缀表达式,以及如何使用栈来对其进行计算,直接去看视频链接。

这里直接给出伪代码

伪代码

stack<int> st;

for(i = 0; i < s.size(); i++)
{
  //遍历字符串,遇见数字加入到栈里,遇见了操作符就从栈内取两个元素进行操作
  if (s[i] == '+' || == '-' || =='/' || == '*')
  { //存入两个数字,并且拿到他们的值
    num1 = st.top(); st.pop();
  	num2 = st.top(); st.pop();
  	//开始运算
  	if(s[i] == +) st.push(num1 + nums2);
  	if(s[i] == -) st.push(num1 - nums2);
    if(s[i] == *) st.push(num1 * nums2);
    if(s[i] == /) st.push(num1 / nums2);
  }
  else st.push(s[i]);
  result = st.top();
  st.pop();
  return result;
}

C++代码

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        // 力扣修改了后台测试数据,需要用longlong
        stack<long long> st; 
        for (int i = 0; i < tokens.size(); i++) {
            if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
                long long num1 = st.top();
                st.pop();
                long long num2 = st.top();
                st.pop();
                if (tokens[i] == "+") st.push(num2 + num1);
                if (tokens[i] == "-") st.push(num2 - num1);
                if (tokens[i] == "*") st.push(num2 * num1);
                if (tokens[i] == "/") st.push(num2 / num1);
            } else {
                st.push(stoll(tokens[i]));
            }
        }

        int result = st.top();
        st.pop(); // 把栈里最后一个元素弹出(其实不弹出也没事)
        return result;
    }
};

  • 10
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《代码随想录知识星球精华-大厂面试八股文v1.1.pdf》是一份介绍八股文的面试指南,针对大厂面试常见题型进行分类,以及给出相应的解答思路。这份资料具有很高的参考价,在为即将面试的职者提供指引的同时,也可以让读者对八股文的思维框架和策略有更好的了解。 这份资料主要包括面试题目的主题分类,如动态规划、字符串、树等,每个分类下又相应给出了典型的问题,如“最长公共子序列”、“二叉树的层次遍历”等等。对于每个问题,都给出了具体的思路指导和代码模板,许多题目给出了多种不同的解法,这些解法包括时间复杂度和空间复杂度的优化。 这份资料对于职者来说,意义重大。在面试,对于某些问题我们可能没有完全解决,但如果有了这份资料,我们在面试也会有一个清晰的思考框架和即时可用的代码模板。此外,这份资料同样适用于对算法数据结构感兴趣的开发者和工程师,对于自学和提高都有帮助。 总之,《代码随想录知识星球精华-大厂面试八股文v1.1.pdf》是一个非常实用的参考材料,它是广大职者和程序员不可或缺的工具,得一读。 ### 回答2: 《代码随想录知识星球精华-大厂面试八股文v1.1.pdf》是一份由知名博主“代码随想”的知识星球推出的热门资料。该资料主要介绍了大厂面试常见的八股文,包括但不限于动态规划、双指针、贪心算法字符串操作等。 通过学习该资料,可以系统地学习和掌握各种常见的算法数据结构,有助于提高自己的代码能力和面试竞争力。此外,资料还给出了一些实际的面试题目例子,对于准备去大厂面试的人来说,是一份非常实用的资料。 当然,要想真正掌握这些算法数据结构,需要自己的不断练习和实践。只有在解决实际问题的过程,才能真正体会到这些算法数据结构的作用和优越性。 总之,该资料对于想要深入了解算法数据结构、提高面试竞争力的人来说是一份得推荐的优秀资料。 ### 回答3: 代码随想录知识星球精华-大厂面试八股文v1.1.pdf是一份关于大厂面试八股文的精华资料,它收集整理了各个大厂面试常见的八股文题目和解题思路,对于准备职或者升职的程序员来说是一份非常有价的资料。 该资料涵盖了常见的算法数据结构、操作系统、计算机网络、数据库、设计模式等知识点,每个知识点都有详尽的讲解和相应的面试题目,可以帮助面试者全面了解每个知识点的考察方向和难点。 此外,该资料还提供了八股文的解题思路和解题方法,强调了实战经验和面试技巧,对于提高面试的成功率也是非常有帮助的。 总之,代码随想录知识星球精华-大厂面试八股文v1.1.pdf是一份非常实用的面试资料,它帮助面试者深入了解各个知识点的考察方向和难点,提高了应对面试的能力和成功率,对于准备职或者升职的程序员来说是一份不可或缺的资料。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值