【数据结构】栈和队列常见题目

这篇具有很好参考价值的文章主要介绍了【数据结构】栈和队列常见题目。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


队列:先进先出 栈:后进先出

队列:先进先出 栈:后进先出

有效的括号

https://leetcode.cn/problems/valid-parentheses/

做法:遍历字符串

1.当前字符是左括号:进栈

2.当前字符是右括号:出栈顶元素和当前字符比较是否匹配

  • 特殊情况:如果此时栈为空,那么说明不匹配

3.最后如果栈为空,说明是匹配的,否则是不匹配的

class Solution {
public:
    bool isValid(string s) {
        stack<char> st;
        //遍历字符串,碰到左括号:进栈   碰到右括号:出栈顶元素判断是否和该右括号匹配
        for(auto& ch:s)
        {
            if(ch == '(' || ch == '[' || ch == '{') 
            {
                st.push(ch);
            }
            else 
            {
                //如果栈为空,说明括号是不匹配的
                if(st.empty()) return false;
                //出栈顶元素和当前右括号匹配
                char top = st.top();
                st.pop();
                if( ch ==')' && top != '('  || ch == ']' && top != '[' ||ch == '}' && top != '{')
                    return false; 
            }
        }
        return st.empty(); //如果最后栈为空,说明括号是匹配的
    }
};

用队列实现栈

https://leetcode-cn.com/problems/implement-stack-using-queues/

【数据结构】栈和队列常见题目,刷题,数据结构

两个队列实现栈

队列:先进先出 栈:后进先出

两个队列:

  • NonEmptyQueue:表示存放数据的队列,记为A
  • EmptyQueue:表示空队列,记为B

插入元素:直接往A队列插入

弹出元素:将A队列的元素都插入到B队列当中,直到A队列只剩下一个元素,此时这个元素就相当于是栈顶元素。然后A队列和B队列交换,保证B队列是空的

例如:插入顺序为1 2 3 4 
A队列:1 2 3 4 
假设要栈顶元素:那么就算A队列队尾元素
假设要弹出栈顶元素:将A的元素放到B,直到A只有一个元素,然后弹出A队列剩余的一个元素就是栈顶元素,然后交换两个队列
    A:4   B:1 2 3  ==> A:1 2 3    B:

获取栈顶元素:本质就是A队列的队尾元素

判空:A队列和B队列为空 就是空

class MyStack {
public:
    MyStack() {

    }
    
    void push(int x) {
        NonEmptyQueue.push(x);//往不为空的队列插入数据
    }
    
    int pop() {
        //将不空队列的数据放到空队列当中,直到不空队列只有一个元素
        while(NonEmptyQueue.size() > 1)
        {
            EmptyQueue.push(NonEmptyQueue.front());
            NonEmptyQueue.pop();
        }
        int front = NonEmptyQueue.front();
        NonEmptyQueue.pop();
        NonEmptyQueue.swap(EmptyQueue);//交换两个队列,保证EmptyQueue为空队列
        return front;
    }
    
    int top() {
        return NonEmptyQueue.back();
    }
    
    bool empty() {
        return EmptyQueue.empty() && NonEmptyQueue.empty();
    }
private:
    queue<int> NonEmptyQueue;//不为空的队列
    queue<int> EmptyQueue;//空队列
};

一个队列实现栈

将队列类比为一个循环圈

插入元素:直接插入

弹出元素:将队列的前size-1个元素重新插入到队列当中,然后此时队头元素就是要弹出的===>队头元素就相当于是栈顶元素

获取栈顶元素:相当于是队尾元素

class MyStack {
public:
    MyStack() {

    }
    
    void push(int x) {
        q.push(x);
    }
    
    int pop() {
        int size = q.size();
        //将前size-1个元素重新插入到队列当中
        for(int i = 0;i<size-1;i++)
        {
            int front = q.front();
            q.pop();
            q.push(front);
        }
        //此时队头元素就相当于是栈顶元素
        int front = q.front();
        q.pop();
        return front;
    }
    
    int top() {
        return q.back();
    }
    
    bool empty() {
        return q.empty();
    }
private:
    queue<int> q;
};

用栈实现队列

https://leetcode-cn.com/problems/implement-queue-using-stacks/

两个栈:

  • pushStack:存放插入数据的栈
  • popStack:存放要弹出数据的栈

插入元素:直接往pushStack插入

子函数Check:检查是否要将pushStack的内容导入到popStack

  • 将pushStack的所有元素插入到popStack ===>元素就变成逆序了
插入顺序:1 2 3 4
pushStack: 1 2 3 4  导入到popStack后:4 3 2 1  符合先进先出

弹出队头元素:先进行检查 =>弹出popStack栈顶元素

获取队头元素:先进行检查 =>返回popStack栈顶元素

class MyQueue {
public:
    MyQueue() {

    }
    
    void push(int x) {
        pushStack.push(x);
    }
    void Check() //检查是否要将push栈的内容导入到pop栈
    {
        if(popStack.empty())
        {
            while(!pushStack.empty())
            {
                popStack.push(pushStack.top());
                pushStack.pop();
            }
        }
    }
    int pop() {
        Check();
        int top = popStack.top();
        popStack.pop();
        return top;
    }
    
    int peek() {
        Check();
        return popStack.top();
    }
    
    bool empty() {
        return pushStack.empty() && popStack.empty();
    }
private:
    stack<int> pushStack;//存放数据的栈
    stack<int> popStack;//用于弹出数据的栈
};

设计循环队列

https://leetcode-cn.com/problems/design-circular-queue/

【数据结构】栈和队列常见题目,刷题,数据结构

方式1:使用数组实现

分析:

1.存储k个元素,需要开辟k+1个空间!否则不能准确的判空和判满

2.需要使用两个变量来标志队头和队尾位置,记为fronttail,还需要一个变量size:记录队列当中存储的元素个数

enQueue: 向循环队列插入一个元素

  • 如果队列为满了,直接插入失败
  • 在tail位置插入元素,然后tail++往后走,注意:需要和size+1取模,防止tail越界

deQueue:从循环队列删除元素

  • 如果队列为空,删除失败
  • 只需要front往后走,front++即可,注意:需要和size+1取模,防止越界

获取队头元素:front指向的就是队头元素

获取队尾元素:注意:tail的前一个位置就是队尾元素!但是不能直接返回:arr[tail-1],因为有可能上一次放的位置是size位置,然后tail++取模后到了0位置,所以要判断一下

  • 如果tail不是0位置:返回arr[tail-1]
  • 如果tail是0位置:返回arr[size]

判断队列是否为满:(tail + 1) % (size+1) == front;

class MyCircularQueue {
public:
    MyCircularQueue(int k) {
        arr.resize(k+1);//开辟k+1个空间
        front = tail = 0;
        size = k;
    }
    
    bool enQueue(int value) {  //向循环队列插入一个元素。如果成功插入则返回真
        if(isFull()) return false;
        arr[tail] = value;
        tail ++;
        tail %= size+1; //tail = tail % (size+1)
        return true;
    }
    
    bool deQueue() { //从循环队列中删除一个元素。如果成功删除则返回真。
        if(isEmpty()) return false;
        front++;
        front %= size+1;
        return true;
    }
    
    int Front() {
        if(isEmpty()) return -1;
        return arr[front];
    }
    
    int Rear() {
        if(isEmpty()) return -1;
        //由于插入元素之后,tail往后走,所以tail的前一个元素才是队尾元素
        if(tail == 0) return arr[size];
        else return arr[tail-1];
    }
    
    bool isEmpty() {
        return front == tail;
    }
    
    bool isFull() {
        return (tail + 1) % (size+1) == front;
    }
private:
    vector<int> arr;
    int front;//标志队头
    int tail;//标志队尾
    int size;//实际存储的元素个数
};

方法2:链表实现

存储k个元素,需要构建k+1个节点的循环链表,还需要定义两个指针front和tail标志队头和队尾,最初链表为空二者都指向链表的头

向循环队列插入一个元素:队列为满,插入失败。否则插入到tail节点位置,然后tail节点往后走

从循环队列删除一个元素:队列为空,删除失败。否则front节点往后走

获取队头元素:返回front指针指向的节点的存放的值

获取队尾元素:注意:tail节点的前一个节点才是队尾节点,所以需要从front位置开始往后遍历找到tail的前一个节点,然后返回对应的值

判空:front == tail

判满:tail->next == front

class MyCircularQueue {
public:
    MyCircularQueue(int k) {
        //构建k+1个节点的循环链表
        tail = head = new ListNode;
        for(int i = 0;i<k;i++) 
        {
            ListNode* newnode = new ListNode;
            tail->next = newnode;
            tail = tail->next;
        }
        //tail指向尾节点,让首尾相连
        tail->next = head;

        //注意:要让tail回到起始位置!!!!!因为一开始链表没有有效元素
        head = tail;
    }
    
    bool enQueue(int value) { //向循环队列插入一个元素。如果成功插入则返回真。
        if(isFull()) return false;
        tail->val = value;
        tail = tail->next;
        return true;
    }
    
    bool deQueue() {
        if(isEmpty()) return false;
        head = head->next;
        return true;
    }
    
    int Front() {
        if(isEmpty()) return -1;
        return head->val;
    }
    
    int Rear() {
        if(isEmpty()) return -1;
        //从head位置遍历查找,tail的前一个节点放的就算队尾元素
        ListNode* cur = head;
        while(cur->next != tail)
            cur = cur->next;
        return cur->val;
    }
    
    bool isEmpty() {
        return head == tail;
    }
    
    bool isFull() {
        return tail->next == head;
    }
private:
    ListNode* head;
    ListNode* tail;
};s

最小栈

https://leetcode.cn/problems/min-stack/

【数据结构】栈和队列常见题目,刷题,数据结构

方法1:使用两个栈,一个数据栈,一个辅助栈

  • 1.如果最小栈为空,就存放当前入栈数据
  • 2.如果最小栈不为空,

case1:如果此时最小栈的栈顶元素>此时入数据栈的当前数 -> 最小栈中压入当前数
case2:如果此时最小栈的栈顶元素<=此时入数据栈的当前数 -> 最小栈中重复压入此时最小栈的栈顶元素

最小栈和辅助栈,同步压入元素,同步弹出元素,每时每刻得到当前数据栈中数据的最小值 ,二者一一对应

class MinStack {
public:
    MinStack() {

    }
    
    void push(int val) {
        dataStack.push(val);
        //判断要往辅助栈放重复元素还是更小的元素
        if( minStack.empty() || minStack.top() >= val)
            minStack.push(val);
        else 
            minStack.push(minStack.top());//重复压入当前最小栈栈顶元素
    }
    
    void pop() {
        int top = dataStack.top();
        dataStack.pop();
        minStack.pop(); //坑点!!此时minStack也要同步pop数据
    }
    
    int top() {
        return dataStack.top();
    }
    
    int getMin() {
        return minStack.top();
    }
private:
    stack<int> minStack;//辅助栈->存放当前栈中对应的最小值
    stack<int> dataStack;
};

优化:

  • 1.如果最小栈为空,就存放当前入栈数据
  • 2.如果最小栈不为空

case1:如果此时最小栈的栈顶元素**>=**此时入数据栈的当前数 -> 最小栈中压入当前数 ,等于的时候也要插入!!!

case2:如果此时最小栈的栈顶元素<此时入数据栈的当前数 -> 不压入数据

pop数据时:如果此时数据栈的数据和最小栈的栈顶数据一样->最小栈出栈顶元素 否则最小栈不出数据

class MinStack {
public:
    MinStack() {

    }
    
    void push(int val) {
        dataStack.push(val);
        if( minStack.empty() || minStack.top() >= val)
            minStack.push(val);
    }
    
    void pop() {
        int top = dataStack.top();
        dataStack.pop();
        if(top == minStack.top())
            minStack.pop(); 
    }
    
    int top() {
        return dataStack.top();
    }
    
    int getMin() {
        return minStack.top();
    }
private:
    stack<int> minStack;//辅助栈->存放当前栈中对应的最小值
    stack<int> dataStack;
};

栈的压入&弹出序列

https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&&tqId=11174&rp=1&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking

【数据结构】栈和队列常见题目,刷题,数据结构

思想就是:使用栈的入栈顺序,模拟这个出栈顺序,如果能模拟出来,就说明是合法的,否则就是非法的!

做法:

使用一个栈,然后遍历pushV数组,模拟入栈顺序,还需要定义一个变量popIndex表示出栈数组遍历到了哪个位置,对于每一次遍历:

  • 先将当前pushV数组的元素进栈
  • 如果栈不为空,并且当前出栈元素 = 栈顶元素,那么就弹出栈顶元素 ==> while循环操作

最后:如果popIndex遍历完了popV数组,说明是可以按这个顺序弹出的,返回true

class Solution {
public:
    //pushV:元素的入栈顺序  popV:判断该序列是否可能为该栈的弹出顺序
    bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
        if(pushV.size() != popV.size()) return false;
        stack<int> st;//模拟进栈顺序
        int popIndex = 0;//当前出栈元素位置
        for(int i = 0;i<pushV.size();i++)
        {
            //当前元素进栈
            st.push(pushV[i]);
            //如果栈不为空,并且当前出栈元素 = 栈顶元素,那么就弹出
            while(!st.empty() && st.top() == popV[popIndex])
            {
                popIndex++;
                st.pop();
            }
        }
        //最后:如果popIndex遍历完了popV数组,说明是可以按这个顺序弹出的
        return popIndex == popV.size();
    }
};

逆波兰表达式(后缀表达式)

https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/

【数据结构】栈和队列常见题目,刷题,数据结构

思路:遍历字符串

  • 如果是操作数:进栈
  • 如果是操作符:取栈顶的两个元素出来进行运算,然后再把运算结果放到栈中
  • 最后遍历完成后,栈顶元素就是计算结果

注意:栈是后进先出的,所以先拿到的是右操作数,然后才是左操作数

最好是使用switch-case语句,因为有+-*/四种情况,后续如果还有其它情况,也比较容易增加。如果使用if-else,看起来不美观

  • 使用第一个字符判断是操作符还是操作数的时候,要特别小心-,因为操作数可能为负数,所以要特判:如果字符串长度为1:说明就是操作符,否则就是操作数
  • 或者说:取字符串的最后一个字符判断是操作数还是操作符 switch(str.back())

stoi函数:用于将string字符串转为整数文章来源地址https://www.toymoban.com/news/detail-656192.html

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        //遍历容器,如果是操作数就进栈
        int left = 0,right = 0;
        auto GetNum = [&]()   //获取两个操作数
        {
            right = st.top(); //注意:先拿到的是右操作数
            st.pop();
            left = st.top();
            st.pop();
        };
        for(auto& str:tokens) //str是string
        {
            //如果是操作数就进栈,如果是操作符就把当前栈中两个元素拿出来进行运算,然后把运算结果进栈
            switch(str[0])
            {
                case '+':
                    GetNum();
                    st.push(left + right);
                    break;
                case '-': //坑点:有可能不是操作符,而是操作数->负数
                    if(str.size() == 1) //操作符
                    {
                        GetNum();
                        st.push(left - right);
                    } 
                    else  //操作数
                    {
                        st.push(stoi(str));
                    }
                    break;
                case '*':
                    GetNum();
                    st.push(left * right);
                    break;
                case '/':
                    GetNum();
                    st.push(left / right);
                    break;
                default: //操作数
                    st.push(stoi(str));
                    break;
            }
        }
        return st.top();
    }
};

到了这里,关于【数据结构】栈和队列常见题目的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 数据结构:栈和队列

    数据结构:栈和队列

    朋友们、伙计们,我们又见面了,本期来给大家解读一下栈和队列方面的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 数据结构专栏:数据结构 个 人 主 页 :  stackY、 目录 前言:  1.栈 1.1栈的

    2024年02月06日
    浏览(10)
  • 数据结构---栈和队列

    栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作,进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。 出栈:栈的删除操作

    2024年01月18日
    浏览(9)
  • 数据结构【栈和队列】

    数据结构【栈和队列】

    一、栈 1.定义:只允许一端进行插入和删除的线性表,结构与手枪的弹夹差不多,可以作为实现递归函数(调用和返回都是后进先出)调用的一种数据结构; 栈顶:允许插入删除的那端; 栈底:固定的,不允许插入或删除; 空栈:不含元素; 2.特点:后进先出; 3.操作:入

    2024年02月15日
    浏览(15)
  • 数据结构——栈和队列

    数据结构——栈和队列

    目录  一.前言 二.前文回顾 三.栈 3.1 栈的概念及结构 3.2 栈的实现 3.2.1 初始化函数 3.2.2 销毁函数 3.2.3 入栈函数 3.2.4 出栈函数 3.2.5 计算大小函数 3.2.6 空栈函数 3.2.7 获取栈顶函数  3.2.8 小测试 3.3 全部代码 四.栈的练习 4.1 有效的括号 五.队列 5.1队列的概念及结构 5.2 队列的实

    2024年01月25日
    浏览(12)
  • 栈和队列【数据结构】

    栈和队列【数据结构】

    2024年02月16日
    浏览(15)
  • 数据结构 | 栈和队列

    数据结构 | 栈和队列

    … 📘📖📃本文已收录至:数据结构 | C语言 更多知识尽在此专栏中! 栈(Stack) 又名堆栈,它是一种运算受限的线性表,限定仅在表尾进行插入和删除操作的线性表。 队列(Queue) 也是一种特殊的线性表,特殊之处在于它只允许在表的前端(Front)进行删除操作,而在表的

    2024年01月23日
    浏览(16)
  • 【数据结构】栈和队列(链表模拟队列)

    【数据结构】栈和队列(链表模拟队列)

      学习本章节必须具备 单链表的前置知识, 建议提前学习:点击链接学习:单链表各种功能函数 细节 详解 本章节是学习用 单链表模拟队列 1. 单链表实现队列 思路如下 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先 进先出

    2024年04月27日
    浏览(15)
  • 数据结构3:栈和队列

    数据结构3:栈和队列

    目录 1.栈 1.1栈的概念及结构 ​1.2栈的实现 2.队列 2.1队列的概念及结构  2.2队列的实现 2.3循环队列 ​3.栈和队列的面试题 4.概念选择题 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除的一端称为栈顶,另一端称为栈底。 栈中数

    2023年04月27日
    浏览(9)
  • 【数据结构】2.栈和队列

    【数据结构】2.栈和队列

    【数据结构】1.线性表的数组描述和链式描述 - imXuan - 博客园 (cnblogs.com) 【数据结构】1.线性表的数组描述和链式描述 - imXuan - 博客园 (cnblogs.com) 队列由一个数组来描述,队首指针是队首的前一个元素,队尾指针是队尾所在元素,当队首和队尾指针重叠时表示队列为空;当 (队

    2024年02月08日
    浏览(12)
  • 数据结构栈和队列

    数据结构栈和队列

    3.1栈和队列的定义和特点 栈和队列是两种常用的、重要的数据结构 栈和队列是限定插入和删除只能在表的 “ 端点 ”进行的 线性表 栈和队列是线性表的子集(是插入和删除位置受限的线性表) 栈的应用: ​ 由于栈的操作具有 后进先出 的固有特性,使得栈成为程序设计中

    2024年02月16日
    浏览(5)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包