代码随想录算法训练营第十天|栈和队列、Leetcode232 用栈实现队列、Leetcode225 用队列实现栈

代码随想录算法训练营第十天|栈和队列、Leetcode232 用栈实现队列、Leetcode225 用队列实现栈

● STL版本

C++STL的版本实现有很多,包括:HP STL、SGI STL、STL Port、P.J.Plauger STL和Rouge Wave STL等。具体在《STL源码剖析》第一章 STL概论与版本简介中1.4 - 1.8中有详细介绍。

三个最普遍的STL版本:
(1)HP STL 其他版本的C++ STL:一般以HP STL为蓝本实现。HP STL是Alexandar Stepanov在惠普Palo Alto实验室工作时,与Meng Lee合作完成的,是C++ STL的第一个实现版本且代码开源;
(2)P.J.Plauger STL 由P.J.Plauger参照HP STL实现出来的,被Visual C++编译器所采用,代码不是开源;
(3)SGI STL 由Silicon Graphics Computer Systems公司参照HP STL实现,主要设计者也是STL之父Alexandar Stepanov,被Linux的C++编译器GCC所采用,SGI STL是开源软件,源码可读性甚高。

其他版本:
(1)STL Port由俄国人Boris Fomitchev通过创建一个免费项目开发STL Port,其目的是为了使SGI STL的基本代码都适用于VC++和C++ Builder等多种编译器。该版本可以跨平台适用于VC++、C++ Builder、GCC等,且能够与BOOST配合使用,实现跨平台代码。STL Port开放源码的;
(2)Rouge Wave STL由Rouge Wave公司参照HP STL实现,用于Borland C++编译器中,这个版本的STL不是开源的。

● 栈和队列

我们需要知道:栈和队列是STL(C++标准库)中的两个数据结构。
我们基于SGI STL里面的数据结构进行讨论其分别的底层实现。

栈(Stack) 是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。
栈
栈提供push和pop等接口,元素操作遵守先进后出的规则,因此栈不提供走访功能,也不提供迭代器。其不能像set和map一样使用iterator遍历元素。
具体在《STL源码剖析》第三章 迭代器概念与traits编程技法中有详细介绍。

迭代器是指针的泛化,允许 C++ 程序使用统一的方式来处理不同的数据结构。 算法对某一种迭代器所指定的值范围起作用,而不是作用于特定的数据类型。 算法可以作用于任何满足迭代器要求的数据结构。

栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的,可以控制使用那种容器来实现栈的功能。因此STL中的栈不能归类为容器,而实container adapter

栈的底层实现可以是vector、deque和list,即主要是数组和链表完成其底层实现。

我们可以指定vector为栈的底层实现:

std::stack<int, std::vector<int> > st;

队列

队列(queue) 是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。
队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
队列
队列同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。
对于常用的SGI STL,如果没有指定底层实现,默认为deque为缺省下队列的底层结构。

我们可以指定list为队列的底层实现:

std::queue<int, std::list<int>> qu;

● Leetcode232 用栈实现队列

题目链接:Leetcode232 用栈实现队列
视频讲解:代码随想录|用栈实现队列
题目描述:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
说明:
你 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

示例 1:
输入:
[“MyQueue”, “push”, “push”, “peek”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

● 解题思路

定义两个stack(_in_out)完成以下操作,_in完成入栈操作,_out完成出栈操作。
(1)void push(int x) 将元素 x 推到队列的末尾:
将入队元素放入_in即可。

(2)int pop() 从队列的开头移除并返回元素:
防止出现错误序列的出队,需要将_in中的元素一次性放入_out中。
_out为空时,我们循环将_in元素放入_out中,然后在_out中进行出栈操作即可。
用栈实现队列

(3)int peek() 返回队列开头的元素:
获取队头元素的代码和第二部的代码基本完全重合,因此可以复用int pop()。但因为在int pop()中已经将元素弹出,但我们只需要获取元素即可,因此需要将保存元素重新压入_out中。

(4)boolean empty() 如果队列为空,返回 true ;否则,返回 false:
_in_out中都没有元素时,证明没有元素在队列中。

时间复杂度:push和empty为O(1), pop和peek为O(n) 空间复杂度:O(n)

● 代码实现

class MyQueue {
public:

    //定义两个栈
    stack<int> _in, _out;

    MyQueue() {
        
    }
    
    //将元素 x 推到队列的末尾
    void push(int x) {
        _in.push(x);
    }
    
    //从队列的开头移除并返回元素
    int pop() {
        if(_out.empty())
        {
            while(!(_in.empty()))
            {
                _out.push(_in.top());
                _in.pop();
            }
        }

        int res = _out.top();
        _out.pop();
        return res;
    }
    
    //返回队列开头的元素
    int peek() {
        int peek = this->pop();
        _out.push(peek); //pop弹出对头元素,返回前需要先push进去
        return peek;
    }
    
    //如果队列为空,返回 true ;否则,返回 false
    bool empty() {
        if(_out.empty() && _in.empty())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue* obj = new MyQueue();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->peek();
 * bool param_4 = obj->empty();
 */

● Leetcode225 用队列实现栈

题目链接:Leetcode225 用队列实现栈
视频讲解:代码随想录|用队列实现栈
题目描述:请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:
你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:
输入:
[“MyStack”, “push”, “push”, “top”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

● 解题思路

方法一:两个队列
完成了Leetcode232 用栈实现队列,在用队列实现栈中我们也可以使用两个队列来完成。
但因为que1只是辅助函数,所以我们在pop()操作完成后还需要将元素放回que1中并清空que2;同时在queue中提供了获取队尾函数back(),所以简化了我们第三个函数的实现。

时间复杂度:pop()为 O(n),其余为O(1) 空间复杂度:O(n)

方法二:一个队列
因为只使用一个队列,我们在pop()中需要将size() - 1个元素重新放入队列中,然后正常执行pop()即可。
队列实现栈

时间复杂度:pop()为 O(n),其余为O(1) 空间复杂度:O(n)

● 代码实现

方法一:两个队列

class MyStack {
public:
    queue<int> que1, que2;
    MyStack() {

    }
    
    //将元素 x 压入栈顶。
    void push(int x) {
        que1.push(x);
    }
    
    //移除并返回栈顶元素。
    int pop() {
        int size = que1.size();
        size--;
        while(size--)
        {
            que2.push(que1.front());
            que1.pop();
        }
        int res = que1.front();
        que1.pop();
        que1 = que2;
        while(!(que2.empty()))
        {
            que2.pop();
        }
        return res;
    }
    
    //返回栈顶元素。
    int top() {
        return que1.back();
    }
    
    //如果栈是空的,返回 true ;否则,返回 false 。
    bool empty() {
        return que1.empty();
    }
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */

方法二:一个队列完成

class MyStack {
public:
    queue<int> que;

    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();
    }
};

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack* obj = new MyStack();
 * obj->push(x);
 * int param_2 = obj->pop();
 * int param_3 = obj->top();
 * bool param_4 = obj->empty();
 */
  • 17
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值