如何找到一个栈中的最小值

如何找到一个栈中的最小值

又是在公众号上看到了一个题目,再一次被虐.感觉自己已经快要习惯了。
题目:实现一个栈,带有出栈(pop),入栈(push),取最小元素(getMin)三个方法。要保证这三个方法的时间复杂度都是O(1)。

当我看到这道题时,脑子里的第一个想法就是:在结构体或对象中设置个指针就好了,让指针永远指向最小值。用c++代码实现就是:

//Stack.h
#ifndef _Stack_H
#define _Stack_H
#include <iostream>
using namespace std;
#define N 10
class Stack{
private:
    int data[N];    //数据
    int top;        //栈顶
    int min;        //最小值指针
public:
    Stack();
    ~Stack();
    void push(int data);    //入栈
    int pop(void);          //出栈
    int getMin(void);       //取数据最小值
};
#endif

乍一看貌似没什么问题,只要在构造函数中初始化一下min=0,然后每次入栈时都要将入栈数据data[top]与data[min]做比较,如果data[top]小于data[min]则必须执行min=top,这样min永远指向栈中最小数据。
但是这样的设计是有问题的。想想是什么问题?

出栈!若当前栈中最小值出栈了,min该指向哪里?没错,它该指向之前栈中的次小值。那么它又要怎么指向次小值?麻烦就来了,无法用O(1)的时间复杂度解决了。
可以看出,这种方法的漏洞在于,当最小值出栈时,无法找到次小值来顶替此时最小值的位置。也就是说只要我们解决这个问题,就可以保证无论怎么入栈出栈,都可以随时返回当前栈中最小值。
如何解决呢?用什么方法可以保证在时间复杂度为O(1)的情况下一次找到次小值?存储!将每一次得到的最小值的下标存储在一个数据结构中,这样就可以在当前最小值出栈时直接找到次小值来顶替当前最小值。至于这个数据结构,我建议是栈。
我们假设之前的栈为Stack_A,这个存储最小值下标的栈叫Stack_B。因为每一次入栈Stack_B的都是Stack_A当前的最小值下标,也就是说,Stack_B的后入栈元素永远比先入栈元素对应的Stack_A数据要小。如果此时Stack_B栈顶元素出栈,Stack_B的top减1,此时的Stack_B栈顶元素为上一次入栈Stack_B时的元素,即当前Stack_A最小值的下标。栈的“First In Last Out”特点,让它成为最适用于当前环境的数据结构。
理论分析完了,我们看代码:

//Stack.h头文件

#ifndef _Stack_H
#define _Stack_H

#include <iostream>
using namespace std;
#define N 10
class Stack_B;
class Stack_A{
private:
    int data[N];    //A数据
    int top;        //A栈顶
    Stack_B *sta;   //B栈指针
public:
    Stack_A();
    ~Stack_A();
    void push(int data);    //入栈
    int pop(void);          //出栈
    int getMin(void);       //取数据最小值
};
class Stack_B{
private:
    int data[N];    //B数据
    int top;        //B栈顶
public:
    Stack_B();
    void push(int index);   //入栈
    int pop(void);          //出栈
    int getTop();           //取栈顶
};

#endif
//Stack.cpp

#include "Stack.h"
/***************   Stack_A    **************************/ 
Stack_A::Stack_A(){
    top=0;              //将栈A栈顶指针置0
    sta=new Stack_B();  //创建栈B对象,用来存储栈A每一次入栈的最小值
}
Stack_A::~Stack_A(){
    delete sta;         //释放sta动态申请空间
}
void Stack_A::push(int data){
    if(top==0||data<=getMin())  //当第一次入栈或入栈数据小于当前最小值时,
        sta->push(top);         //将当前栈顶指针入Stack_B栈
    this->data[top++]=data;     //将数据入Stack_A栈
}
int Stack_A::pop(){
    if(sta->getTop()==--top)    //若出栈时Stack_B栈顶元素为Stack_A的top-1值,即Stack_A出栈时最小为当前元素时,
        sta->pop();             //Stack_B也出栈
    return data[top];           //返回栈顶元素
}
int Stack_A::getMin(){
    return data[sta->getTop()]; //返回Stack_A最小元素
}
/********************************************************/

/****************   Stack_B   ***************************/
Stack_B::Stack_B(){
    top=0;              //将栈B栈顶指针置0
}
void Stack_B::push(int index){
    this->data[top++]=index;    //将栈A的当前最小值下标入栈B
}
int Stack_B::pop(){
    return data[--top];         //栈B栈顶元素出栈
}
int Stack_B::getTop(){
    return data[top-1];         //返回栈B栈顶元素,即返回当前Stack_A最小值下标
}
/*******************************************************/
//client客户端测试数据
#include "Stack.h"
int main(int argc, char *argv[])
{
    Stack_A* p=new Stack_A();   //动态创建Stack_A对象
    p->push(5);                 //入栈
    p->push(12);
    p->push(3);
    cout<<"getMin:"<<p->getMin()<<endl; //显示最小值
    cout<<"pop:"<<p->pop()<<endl;       //出栈
    cout<<"pop:"<<p->pop()<<endl;
    cout<<"getMin:"<<p->getMin()<<endl;
    delete p;
    return 0;
}
  • 实验结果
    这里写图片描述

当然了,如果对这个过程理解的不是很深刻的话,就会像我一样,被人家说一句“如果入栈元素大于min小于次小值呢”就开始陷入沉思一脸懵逼怀疑自己怀疑人生。
为了证明我真的参悟了这个算法,我要解释一下:如果入栈元素大于min的话,不管它小于谁,出栈的时候它一定比min先出栈;而想要查看正确的次小值,就一定要min先出栈。脑子不要被人一问就混乱了,而忘记这个出栈顺序。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值