栈队列OJ题分享及讲解

文章目录

    • 1、有效的括号
    • 2、用栈实现队列
    • 3、用队列实现栈
    • 4、设计循环队列

从今天开始后面的OJ题,我一致用C++来带大家实现,这需要大家先去了解C++STL里面的容器,以及相关的接口。

1、有效的括号

栈队列OJ题分享及讲解_第1张图片
这题是比较典型的用栈实现的题目,给了我们一个字符串s,让我们去判断里面的括号是否一一匹配。
解题思路:

class Solution {
public:
    bool isValid(string s) {
        stack<char> st;
        for(int i=0;i<s.size();i++)
        {
            if(s[i]=='['||s[i]=='{'||s[i]=='(')
            {
                st.push(s[i]);
            }
            else if(!st.empty()&&(s[i]==']'&&st.top()=='['
            ||s[i]=='}'&&st.top()=='{'
            ||s[i]==')'&&st.top()=='('))
            {
                st.pop();
            }
            else
            {
                return false;
            }
        }
        return st.empty();
    }
};

首先我们创建一个栈st,当然大家也可以用数组去模拟一个栈,去遍历这个字符串,如果遇到左括号就都将入栈,否则如果是右括号,我们就需要和栈顶的元素给比较一下是否匹配,就是s[i]和st.top(),当然这里也一个需要注意的问题,比较的时候栈是不能为空,所以这里得多加一个判断,判断栈不能为空,也就是!st.empty(),如果两个条件都不满足,那就说明和右括号不匹配,直接返回false,最后出来的时候还需要判断一下栈是否为空,因为为空就说明一定是都匹配的括号,因为要避免左括号数量大于右括号的时候,出来栈不为空,就说明该字符串无效,当然我们只需要返回st.empty()即可,当为空是会返回真,反之返回假。
运行结果如下:
栈队列OJ题分享及讲解_第2张图片

2、用栈实现队列

栈队列OJ题分享及讲解_第3张图片
这题就是给了我们几个队列的接口,让我们用两个栈去实现队列,并且让几个接口到达队列的要求。
解题思路:
我们可以让一个栈用来入数据,一个栈用来出数据,这边我画个图给大家理解一下。

入队列:
栈队列OJ题分享及讲解_第4张图片
出队列:栈队列OJ题分享及讲解_第5张图片
如上图有,st1和st2两个栈,我们让st1来入数据,出数据的时候我们只需要将st2栈顶的元素给pop即可。

class MyQueue {
public:
    MyQueue() {
    }

    void push(int x) {
        st1.push(x);
    }
    
    int pop() {
        int top=0;
        if(st2.empty())
        {
            while(!st1.empty())
            {
                st2.push(st1.top());
                st1.pop();
            }
            top=st2.top();
        }
        else
        {
            top=st2.top();
        }
        st2.pop();
        return top;
    }

    int peek() {
        int top=0;
        if(st2.empty())
        {
            while(!st1.empty())
            {
                st2.push(st1.top());
                st1.pop();
            }
            top=st2.top();
        }
        else
        {
            top=st2.top();
        }
        return top;
    }
    
    bool empty() {
        return st1.empty()&&st2.empty();
    }
    stack<int> st1;
    stack<int> st2;
};

这里我在类里面定义了两个栈st1和st2,第一个接口MyQueue()我们不需要实现,因为这是这个类的构造函数,但是我们的栈会去调用直接的构造函数初始化,所以我们不需要实现。
第二个接口就是push,也就是往队列里面入数据,这里我们只需要调一下st1入栈的接口st1.push(x)将x值存入栈中。
第三个接口pop就是让我们出数据,但这里规定还要返回出的数据,按照我们刚刚的思路将st1里面的数据导入st2里面,但这里有一个需要注意的点,当st2不为空的时候,说明数据还没有出完,所以我们不需要再入数据到st2,要不出数据的时候会出错数据,我们只需要先将st2栈顶数据保存起来,然后再将栈顶数据删除返回即可。
第四个接口和第三个接口是差不多的,这是不需要删除数据而且,这里我就不多说了。
第五个接口也是比较简单的,就是判断队列是否为空,我们只需要判断两个栈是不是都为空,因为st1不为空说明还有数据没有入完,st2不为空说明还有数据没有出完。
运行结果如下:
栈队列OJ题分享及讲解_第6张图片

3、用队列实现栈

栈队列OJ题分享及讲解_第7张图片

这一题和上一题恰恰相反,用两个队列实现栈,实现栈的四个接口即可。
解题思路:
我们入栈的时候,需要将数据入入不为空的那个队列,而出栈的时候需要将不为空的队列的数据导入为空的队列里面,但里面要留下最后一个,就是我们需要删除的数据,还是给大家画图理解一下:
入栈:
栈队列OJ题分享及讲解_第8张图片
出栈:
栈队列OJ题分享及讲解_第9张图片
如上图,入栈的时候将数据全部入入不为空的队列q1,出的时候我们需要将q1里面的前4个数据导入q2,最后q2里面剩下来的5就是我们需要删除的数据。

class MyStack {
public:
    MyStack() {
    }
    
    void push(int x) {
        if(!_q1.empty())
        {
            _q1.push(x);
        }
        else
        {
            _q2.push(x);
        }
    }
    
    int pop() {
         queue<int>* emptyQ=&_q1;
         queue<int>* nonemptyQ=&_q2;
         if(!_q1.empty())
         {
             swap(emptyQ,nonemptyQ);
         }
         while(nonemptyQ->size()>1)
         {
             emptyQ->push(nonemptyQ->front());
             nonemptyQ->pop();
         }
         int top=nonemptyQ->front();
         nonemptyQ->pop();
         return top;
    }
    
    int top() {
        if(!_q1.empty())
        {
            return _q1.back();
        }
        else
        {
            return _q2.back();
        }
    }
    
    bool empty() {
        return _q1.empty()&&_q2.empty();
    }
    queue<int> _q1;
    queue<int> _q2;
};

这里我先在类里面定义了两个队列_q1和_q2,第一个函数和上面一样因为我们的队列会去调用自己的构造函数初始化,所以什么都不需要写。
第二个push就是要我们入栈并返回栈顶元素,我们只需要判断一下谁不为空,然后往里面入数据即可,当然一开始都为空,那就随便入一个队列里面。
第三个函数pop就是要我们出栈,这里我先是定义了两个stak*类型的指针,分别指向两个栈,这么我默认q1为空,然后做一次判断,如果q1不为空,就交换两个指针指向的空间,然后将指向q2的指针往指向q1的指针入数据,然后我们保存栈顶的元素,并将该数据删除,最后返回保存的元素top即可。
第四个函数就是要我们返回栈顶的元素,我们只需要拿到不为空队列的最后一个元素,所以这里然后_q1为空就返回_q2.back(),反之返回_q1.back()。
第五个函数判断栈是否为空,两个栈都为空就说明没有数据可出了,即为空。
运行结果如下:
栈队列OJ题分享及讲解_第10张图片

4、设计循环队列

栈队列OJ题分享及讲解_第11张图片
这个题我们需要注意的问题是,实现的队列是循环的,而空间是固定的,另外题目也限制了我们不能用C++内置的队列库,所以我们这里有两个实现方法,用数组或者链表,我自己的话用的是数组。
解题思路:
我们可以用一个数组,首先开固定的大小,并且给两个变量_front和_tail,存放头和尾的下标
如图:
栈队列OJ题分享及讲解_第12张图片

当里面没有元素的时候front和tail下标是相等,但当里面有数据的时候tail是最后一个数据的下一个位置的下标。
当我们要出队列的时候是需要改变_front的下标的,另外当_tail等于数组能存放数据个数的时候,如果这时还要插入的话,要检查数组是否满了,且得改变_tail的下标,所以我又加了一个_size计算数组有效的数据个数。
如图:
栈队列OJ题分享及讲解_第13张图片
如上面这种情况,第一个数据已经被出了,且tail已经走到最后一个数据下标的下一个位置了,这时我们要将数组的下标改为0,才能继续进行插入,否则会越界。

class MyCircularQueue {
public:
    MyCircularQueue(int k) {
        v.resize(k);
        _front=_tail=_size=0;
        _k=k;
    }
    
    bool enQueue(int value)
    {
        if(_size==_k)
           return false;
        if(_tail==_k)
        {
            v[0]=value;
            _tail=0;
        }
        else
        {
            v[_tail]=value;
        }
        _tail++;
        _size++;
        return true;  
    }
    
    bool deQueue() {
        if(_size==0)
          return false;
        if(_front==_k)
        {
            _front=1;
        }
        else
        {
            _front++;
        }
        _size--;
        return true;
    }
    
    int Front() {
        if(_size==0)
        {
            return -1;
        }
        if(_front==_k)
          return v[0];

        return v[_front];
    }
    
    int Rear() {
        if(_size==0)
        {
            return -1;
        }
        return v[_tail-1];
    }
    
    bool isEmpty() {
        return _size==0;
    }
    
    bool isFull() {
        return _size==_k;
    }
    vector<int> v;
    int _k;
    int _size;
    int _front;
    int _tail;
};

我给大家一个一个接口来分析:
1、MyCircularQueue(int k),它给的k就是数组需要开的大小,我们需要将_size,_tail_front,一开始都值为0,再将数组v开成k个数据的大小,将k赋值给_k,这个_k呢,是数组大小,后面的接口都用得到。
2、bool enQueue(int value),就是要入队列,插入成功返回真,反之返回假,我们得先判断一下_size是否等于_k,如果等于我们就直接返回false,接下来插入数据的逻辑,如果_tail已经等于_k了那说明我们需要在下标为0这个位置插入value,把_tail置为0,反之只需要在_tail这个位置插入即可,最后将_tail++和_size++,返回true。
3、bool deQueue(),就是要我们出队列,即改变_front,进来先判断_size是否为0,如果为0没数据可出,直接返回false,接下来也有两种情况需要判断
如果_front==_k,跟我们上面将的一样,数组下标的最后一个位置+1其实就是0,而这时候_front已经在最后一个位置的下一个了,所以这里将_front置为1,否则我们只需要将_front++即可,最后_size–一下,返回true。
4、int Front(),这个是要我们去队头的数据,上面要求如果队列为空就返回-1,所以我这里判断了一下,另外这里如果_front==_k的时候返回下标为0这个位置就行了。
5、int Rear()取队尾的数据,这里我们只需要返回_tail-1这个位置的数据就行了,不需要像上面一样做特殊判断,因为_tail本来就是最后一个数组的下一个位置。
最后面两个接口的话就都比较简单了,判断是否是空和判断是否满了,分别只需要判断_size是否等于0,判断_size是否等于_k即可。
运行结果如下:
栈队列OJ题分享及讲解_第14张图片
这就是本篇博客的全部内容了,如果觉得博主的文章还不错的话,请点赞+收藏⭐️+留言支持一下博>主哦。
栈队列OJ题分享及讲解_第15张图片

你可能感兴趣的:(leetcode,算法,c++,数据结构)