17_7_19:实现一个栈要求实现入栈、出栈、获得最小值的时间复杂度为O(1)

问题描述:
实现一个栈Stack,要求实现Push(出栈)、Pop(入栈)、Min(返回最小值的操作)的时间复杂度为O(1) 。

难点分析:
首先,我们知道,在栈的实现中,入栈、出栈操作的时间复杂度都是O(1)。
难就难在返回最小值的操作的时间复杂度。如果有一个值来存储的话,到时可以,但是,当有入栈,出栈操作时,这个最小值是不断变换着的。
所以,难点可以看成是:最小值如何存储,才能满足栈中元素的动态变换呢?

思路:
虽然栈中元素可能动态变换,但是也要满足“后进先出”的原则。
当入栈的元素,小于存储的最小值时,我们要更新最小值;当最小值出栈时,我们也要更新最小值。
也就是说,我们也要动态的存储栈中出现过的最小值,知道这个元素出栈。

这样,我们可以利用两个栈s1,s2,一个专门存储数据集合,一个专门存储数据中的最小值集合。
当一个数据data入栈时,存储到第一个栈s1中,并且和第二个栈s2中的栈顶元素s2.top()比较大小。如果data<=s2.top(),那么也存储到s2中,这样当返回最小值时,直接就可以从s2中取,时间复杂度O(1)。
当一个数据data出栈时,和第二个栈中元素s2.top()比较,如果data=s2.top。则说明,此时出栈的元素是最小值。那么就要s1,s2都要出栈。来更新数据栈,以及最小值栈。此时,返回最小值时,直接就可一个从s2中取,时间复杂度O(1)。
可以发现,利用栈的“后进先出”规则,可以用来,动态更新最小值结婚。并且,位于栈顶的永远是最小的。从上往下,最小值数据依次变大。

值得注意的是:数据入栈时和最小值栈顶元素比较时,一定是“<=”,这样可以避免多个值相同数据,出栈时造成的错误。

实现代码:

#include <iostream>
#include <stack>
#include <cassert>
#include <time.h>  //用来生成随机数

template <typename T>
class Stack
{
public:
    void Push(const T& data)
    {
        _s_1.push(data);

        //当_s_2为空时,_s_1原来肯定也为空,实现的栈中数据,只有一个。
        if (_s_2.empty())
        {
            _s_2.push(data);
            return;
        }

        //如果,入栈数据小于等于实现栈中的最小值,也入栈
        T &min = _s_2.top();
        if (data <= min)
            _s_2.push(data);
    }

    void Pop()
    {
        if (_s_1.empty()) //_s_1为空,_s_2也为空,当做一种情况。
            assert(0);

        //如果出栈元素,等于最小值,则,两个一起出栈,否则,只有数据出栈,最小值不变
        if (_s_1.top() == _s_2.top())
            _s_2.pop();
        _s_1.pop();
    }

    T& Get_Min()
    {
        if (_s_2.empty())
            assert(0);

        return _s_2.top();
    }

private:
    std::stack<T> _s_1;
    std::stack<T> _s_2;
};

测试用例:

int main()
{
    Stack<int> s;

    srand((unsigned)time(NULL)); //以时间作为随机数种子
    for (int i = 0; i < 100; ++i)
        s.Push(rand());

    //测试用例,输出应该是从小到大递增
    for (int i = 0; i < 100; ++i)
    {
        std::cout <<i<<":"<< s.Get_Min() << std::endl;
        s.Pop();
    }

    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值