代码随想录day11-栈与队列(1)

文章介绍了栈和队列的基础概念,如FILO(先进后出)和FIFO(先进先出),并探讨了C++中stack和queue作为容器适配器而非容器的特性。通过LeetCode的题目,如232题(用栈实现队列)、225题(用队列实现栈)和20题(有效括号),展示了如何利用栈和队列解决实际编程问题。此外,还讨论了如何使用栈解决括号匹配问题,并提供了不同的解题策略。
摘要由CSDN通过智能技术生成

栈与队列基础:
栈和队列的基本思想:栈是先进后出(Fisrt in Last out, FILO) 的,而队列是先进先出(Fisrt in Last out, FILO) 。栈只有栈顶top可以被外界访问,所以栈是没有迭代器,也是不支持随机访问的,即栈是不支持遍历操作的。
队列只有队头队尾可以被外界访问的,同样也是不支持随机访问,没有迭代器。

c++中的stack和queue到底是不是容器呢?
不是容器。栈和队列可以理解为一种思想,而具体的实现通过vector,list以及deque都可以实现。而c++中的stack以及queue只能说是容器适配器而不是容器,通常使用deque实现。

代码随想录day11-栈与队列(1)

1、LeetCode 232 用栈实现队列

题目分析:
本题本身不考虑什么算法,考虑对栈和队列的熟悉情况。我们使用后两个栈来实现,一个作为输入栈,一个作为输出栈。输入栈存放数据,输出栈是输入栈反着存的数据,用来模拟队列的先进先出。

题目解答:

class MyQueue {
public:
    MyQueue() {

    }
    
    void push(int x) {
        stkIn.push(x);
    }
    
    int pop() {
        // 将stkIn的东西存到stkOut中
        if (stkOut.empty()) {  // 当stkIn为空的时候,才往里面压入数据
            while (!stkIn.empty()) {
                stkOut.push(stkIn.top());
                stkIn.pop();
            }
        }
        int res = stkOut.top();
        stkOut.pop();
        return res;
    }
    
    int peek() {
        // 使用pop()的方法
        int res = pop();
        stkOut.push(res);  // 由于使用pop将其弹出来了,这里需要再压进去
        return res;
    }
    
    bool empty() {
        return stkOut.empty() && stkIn.empty();  // 二者都为空才是空
    }

private:
    stack<int> stkIn;  // 输入的栈
    stack<int> stkOut;  // 输出的栈
};

2、LeetCode 225 用队列实现栈

题目分析:
题目要求使用两个队列来实现栈,其实使用一个队列就可以解决了。使用两个队列,第二个就是对第一个队列的暂存。

题目解答:
使用两个队列的思路:

class MyStack {
public:
    MyStack() {

    }
    
    void push(int x) {
        que1.push(x);
    }
    
    int pop() {
        int n = que1.size();
        n--;  // 保留最后一个弹出并返回 
        while (n) {
            que2.push(que1.front());
            que1.pop();
            n--;
        }
        // 此时就是最后一个
        int res = que1.front();
        que1.pop();
        que1 = que2;  // 将que2赋值给que1
        while (!que2.empty()) que2.pop();
        return res;
    }
    
    int top() {
        return que1.back();
    }
    
    bool empty() {
        return que1.empty();
    }

private:
    queue<int> que1;
    queue<int> que2;
};

其实以上代码用一个队列完全可以实现,并且实现的方法完全一样。

class MyStack {
public:
    // 尝试使用一个队列来实现
    MyStack() {

    }
    
    void push(int x) {
        que.push(x);
    }
    
    int pop() {
        // 其实需要弹出栈顶,对于队列而言,就是其最后一个,我们只需要把前面的所有元素都移到后面去就行
        int size = que.size();
        size--;  // 提前留好一个元素
        while (size--) {
            que.push(que.front());  // 将后面的元素再搞到后面去
            que.pop();
        }
        int res = que.front();  // 此时第一个就是需要弹出的元素
        que.pop();
        return res;
    }
    
    int top() {
        return que.back();
    }
    
    bool empty() {
        return que.empty();
    }
private:
    queue<int> que;
};

3、LeetCode 20 有效的括号

题目分析:
其实这道题目非常简单,就是一直不想思考,心烦意乱。
本题就是判断括号是不是符合规定,其实我们遇到左括号入栈,遇到右括号判断栈顶的是不是匹配,如果不匹配或者直接没元素了,就返回false。否则栈为空,就代表是符合规定的。
注意细节:如果是奇数的话,直接返回false即可。

题目解答:

class Solution {
public:
    bool isValid(string s) {
        stack<char> stk;
        if (s.length() % 2 != 0) return false;
        for (int i = 0; i < s.length(); i++) {
            if (s[i] == '(' || s[i] == '[' || s[i] == '{') stk.push(s[i]);
            else if (s[i] == ')') {
                // 如果没有左括号或者左括号不匹配,直接返回false
                if (stk.empty() || stk.top() != '(') return false;
                else stk.pop();
            }
            else if (s[i] == ']') {
                if (stk.empty() || stk.top() != '[') return false;
                else stk.pop();
            }
            else {
                if (stk.empty() || stk.top() != '{') return false;
                else stk.pop();
            }
        }
        return stk.empty();
    }
};

卡尔的思路比较巧妙,他将不符合情况的题目分为三种,即

  • 第一种情况:字符串里左方向的括号多余了 ,所以不匹配,如( ( ){ }[ ];
  • 第一种情况:括号数量对的,但是不匹配,如( [ ) ];
  • 第三种情况:字符串里右方向的括号多余了 ,所以不匹配,如 ( ){ }[ ] ) )。

然后在遇到左括号的时候,压入相应的右括号,最后只需要判读是不是和栈顶元素相等即可,简化了左括号入栈的代码。解答如下:

class Solution {
public:
    bool isValid(string s) {
        // 使用栈的思路来解决问题
        stack<char> stk;
        if (s.length() % 2 != 0) return false; // 如果是奇数,直接返回即可
        for (int i = 0; i < s.size(); i++) {
            if (s[i] ==  '(') stk.push(')');  // 将其匹配的括号压入栈,只需要判断想不想等即可,漂亮的思路呢
            else if (s[i] == '[') stk.push(']');
            else if (s[i] == '{') stk.push('}');
            // 下面的else if就是右括号的情况了
            // 下面的语句包括了第二,第三种情况
            else if (stk.empty() || s[i] != stk.top()) return false;
            else stk.pop();  // 如果相等,就弹出
        } 
        return stk.empty();  // 第一种情况,如果栈为空,就代表匹配上了,否则就不为空,返回false
    }
};

4、LeetCode 1047 删除字符串中的所有相邻重复项

题目分析:
如果不知道用栈的话,本题可以这样思考。可以使用我们之前学的滑动窗来思考。最开始两个指针都指向开始,然后右指针遍历数组,不重复的话,左指针也更新,一旦遇到了重复的,右指针移动,直到遇到不重复的,左指针移动。这里就牵涉到一个问题:
怎么确定当前是不是重复的呢,怎么存呢,如果是的话,刚刚没存怎么回去,如果存了,中间的元素一删除,又出现了元素怎么办?
这就是栈的优势所在了,不管重复与否,我先入栈,然后遇到了元素和栈顶的相等,就代表是重复的,弹出即可。最终剩在栈种的元素就是不重复的元素。

题目解答:

class Solution {
public:
    string removeDuplicates(string s) {
        // 典型的栈的思路解决问题
        stack<int> stk;
       string ans;
        for (auto& ch : s) {
            if (!stk.empty() && ch == stk.top()) stk.pop();  // 如果和栈顶元素相等,弹出
            else stk.push(ch);
        }
        while (!stk.empty()) {
            ans += stk.top();
            stk.pop();
        }
        reverse(ans.begin(), ans.end());  // 出来的结果的反的
        return ans;
    }
};

直接用字符串实现栈,还可以省反转的时间:

class Solution {
public:
    string removeDuplicates(string s) {
        string ans;
        for (const auto& ch : s) {
            if (!ans.empty() && ans.back() == ch) ans.pop_back();
            else ans.push_back(ch);
        }
        return ans;
    }
};

string也是有empty(),back(),push_back()以及pop_back()的。
今天对栈和队列有了点儿初步的认知,主要就是两个性质的使用。

苦难是人生的老师。——巴尔扎克

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值