代码随想录DAY10 - 栈与队列 - 08/09

目录

栈与队列理论基础

队列

C++的容器适配器

用栈实现队列

题干

思路

方法一:让队头元素对应栈顶,队尾元素对应栈底

方法二:使用输入栈和输出栈

代码

方法一:让队头元素对应栈顶,队尾元素对应栈底

方法二:输入栈和输出栈

用队列实现栈

题干

思路

方法一:使用一个队列实现栈

方法二:使用两个队列实现栈

代码

方法一:使用一个队列实现栈

方法二:使用两个队列实现栈


栈与队列理论基础

问题:

  1. C++中stack 是容器么?

  2. 我们使用的stack是属于哪个版本的STL?

  3. 我们使用的STL中stack是如何实现的?

  4. stack 提供迭代器来遍历stack空间么?

栈和队列是C++标准库(STL)里提供的两个数据结构。

栈符合先进后出的原则,C++中的栈提供 push 和 pop 接口。栈不提供走访功能,也不提供迭代器,因此也无法用迭代器遍历所有元素。栈是用底层容器来完成所有工作,对外提供统一接口,而底层容器是可插拔的,即可以控制使用哪种容器来实现栈的功能。栈的底层实现可以是vector,deque,list等。

学习参考:https://c.biancheng.net/view/6971.html

C++中常见的栈操作:

成员函数功能
empty()当 stack 栈中没有元素时,该成员函数返回 true;反之,返回 false。
size()返回 stack 栈中存储元素的个数。
top()返回一个栈顶元素。如果栈为空,程序会报错。
push(const T& val)将元素压入栈顶。
pop()弹出栈顶元素。

队列

队列中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器。所以STL 队列也不被归类为容器,而被归类为 container adapter(容器适配器)。

学习参考:C++ STL queue容器适配器详解

C++中队列的常见操作:

成员函数功能
empty()如果 queue 中没有元素的话,返回 true。
size()返回 queue 中元素的个数。
front()返回 queue 中第一个元素。
push(const T& obj)在 queue 的尾部添加一个元素。
pop()删除 queue 中的第一个元素

C++的容器适配器

学习参考:什么是适配器,C++ STL容器适配器详解

C++中的序列式容器有很多,如 vector、list、deque,而容器适配器就是通过封装某个序列式容器,并重新组合该容器中包含的成员函数,使其满足某些特定场景的需要。在STL中的容器适配器,其内部使用的基础容器并不是固定的,用户可以在满足特定条件的多个基础容器中自由选择。在C++中,栈适配器 stack 和队列适配器 queue 默认使用的底层容器是 deque 双端队列

容器适配器基础容器筛选条件默认使用的基础容器
stack

基础容器需包含以下成员函数:

empty()

size()

back()

push_back()

pop_back()

满足条件的基础容器有 vector、deque、list。

deque
queue

基础容器需包含以下成员函数:

empty()

size()

front()

back()

push_back()

pop_front()

满足条件的基础容器有 deque、list。

deque

用栈实现队列

题干

题目:请你仅使用两个栈实现先入先出队列。应当支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

push(x) -- 将一个元素放入队列的尾部。 pop() -- 从队列首部移除元素。 peek() -- 返回队列首部的元素。 empty() -- 返回队列是否为空。

你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。

链接:. - 力扣(LeetCode)

思路

方法一:让队头元素对应栈顶,队尾元素对应栈底

①push(x) -- 将一个元素放入队列的尾部:队列是先进先出的,所以将元素放入队列尾部,就是最晚弹出,最晚弹出就相当于要放到栈的最底层。要将元素放到栈的底层,就得先把原栈里的所有元素弹出,并暂存在另一个栈空间中,再将要插入的元素压入原栈,此后把暂存在另外栈空间中的其他元素压回原栈中。时间复杂度O(2n)

②pop() -- 从队列首部移除元素:既然栈顶元素存放的是队头,那么直接弹出栈顶元素即可。时间复杂度O(1)

③peek() -- 返回队列首部的元素:从栈顶读出元素。时间复杂度 O(1)

④empty() -- 返回队列是否为空:和判断栈是否为空是相同的,栈空说明队列也空。时间复杂度O(1)

方法二:使用输入栈和输出栈

Q1:如何理解输入栈和输出栈?

输入栈就相当于队列的入队入口,输出栈就相当于出队出口。

Q2:如何使用输入栈和输出栈完成对应的队列操作?

①当 push(x) 操作要插入元素到队尾时,直接压入 输入栈 中。

②当要 pop() 弹栈 或读取队首元素 peek() 时,如果输出栈为空,则把输入栈的所有元素压入输出栈中,再从输出栈弹出元素;若输出栈不为空,则直接弹出输出栈的栈顶。

③如何判断队列是否为空?由于我们使用了两个栈同时存储队列元素,那么只有当两个栈都为空时队列才为空。

代码

方法一:让队头元素对应栈顶,队尾元素对应栈底
class MyQueue {
public:
    stack<int> myQueue;
   
    // 创建、初始化队列
    MyQueue() {
    }
    // 插入元素到队尾
    void push(int x) {
        stack<int> tmp;
        // 将栈中其他元素暂存在另一个栈中,清空栈再插入元素
        while (!myQueue.empty()){
            tmp.push(myQueue.top());
            myQueue.pop();
        }
        // 在栈底插入新元素
        myQueue.push(x);
        // 将暂存的其他元素压回原栈
        while (!tmp.empty()){
            myQueue.push(tmp.top());
            tmp.pop();
        }
    }
    // 从栈的首部弹出队头元素
    int pop() {
        int num = myQueue.top();
        myQueue.pop();
        return num;
    }
    // 返回队列首部的元素
    int peek() {
        return myQueue.top();
    }
    // 判断队列是否为空
    bool empty() {
        return myQueue.empty();
    }
};
方法二:输入栈和输出栈
class MyQueue {
public:
	stack<int> in;
    stack<int> out;
    MyQueue() {

    }
	
    // 时间复杂度 O(1)
    void push(int x) {
        in.push(x);
    }

    int pop() {
        // 时间复杂度为 O(n)
        int num = peek();
        out.pop();
        return num;
    }

    int peek() {
        if (out.empty()){
            // 要遍历整个栈,所以时间复杂度为 O(n)
            while (!in.empty()){
                out.push(in.top());
                in.pop();
            }
        }
        int num = out.top();
        return num;
    }
	
    // 时间复杂度 O(1)
    bool empty() {
        if (in.empty() && out.empty()){
            return true;
        } else{
            return false;
        }
    }
};

用队列实现栈

题干

题目:请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。你只能使用队列的标准操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。

实现 MyStack 类:

  • push(x) -- 元素 x 入栈

  • pop() -- 移除栈顶元素

  • top() -- 获取栈顶元素

  • empty() -- 返回栈是否为空

链接:. - 力扣(LeetCode)

思路

方法一:使用一个队列实现栈

当使用一个队列实现栈时,关键操作就是弹出的元素应该是队尾元素。如果队列的长度是 size,那么就需要把队尾元素的前 size - 1 个元素依次从队头弹出又插入回队尾,此时之前的队尾元素就会移动到队头了。

方法二:使用两个队列实现栈

使用两个队列,关键是在每次插入新元素时使新元素变成队头,弹栈时只需弹出非空队列的队头元素,这样就和栈的后进先出原则符合。

如何实现?我们需要保持两个队列中的一个队列为空,每次插入新元素时都插入到空队列,这样新元素就可以作为空队列的队头,而剩下的另一个队列中的元素依次弹出插入到新元素的末尾,这样就可以一直保持每次插入的新元素在队头的位置。

代码

方法一:使用一个队列实现栈
class MyStack {
public:
    queue<int> stack;
    MyStack() {
    }
    void push(int x) {
        stack.push(x);
    }
    
	// pop() 时间复杂度O(n)
    int pop() {
        int count = stack.size();
        // 将除了队尾以外的前面所有元素重新插入队尾,也就是 count - 1 个元素
        while (count > 1){
            int tmp = stack.front();
            stack.pop();
            stack.push(tmp);
            count--;
        }
        // 此时队头就是要弹出的元素
        int num = stack.front();
        stack.pop();
        return num;
    }

    int top() {
        int num = pop();
        stack.push(num);
        return num;
    }

    bool empty() {
        return stack.empty();
    }
};
方法二:使用两个队列实现栈
class MyStack {
public:
    queue<int> que1;
    queue<int> que2;
    MyStack() {
    }
    
    // 时间复杂度 O(n)
    void push(int x) {
        if (que1.empty() && que2.empty()){
            que1.push(x);
        } else if (que1.empty()){
            que1.push(x);
            while (!que2.empty()){
                que1.push(que2.front());
                que2.pop();
            }
        } else if (que2.empty()){
            que2.push(x);
            while (!que1.empty()){
                que2.push(que1.front());
                que1.pop();
            }
        }
    }

    int pop() {
        int num;
        if (!que1.empty()){
            num = que1.front();
            que1.pop();
        } else if (!que2.empty()){
            num = que2.front();
            que2.pop();
        }
        return num;
    }

    int top() {
        int num;
        if (!que1.empty()){
            num = que1.front();
        } else if (!que2.empty()){
            num = que2.front();
        }
        return num;
    }
    
	// 当两个队列都为空时才说明栈空
    bool empty() {
        return que1.empty()&&que2.empty();
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值