⭐算法入门⭐《栈 - 单调栈》简单01 —— LeetCode 155. 最小栈

Posted 英雄哪里出来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了⭐算法入门⭐《栈 - 单调栈》简单01 —— LeetCode 155. 最小栈相关的知识,希望对你有一定的参考价值。

🙉饭不食,水不饮,题必须刷🙉

C语言免费动漫教程,和我一起打卡!
🌞《光天化日学C语言》🌞

LeetCode 太难?先看简单题!
🧡《C语言入门100例》🧡

数据结构难?不存在的!
🌳《画解数据结构》🌳

LeetCode 太简单?算法学起来!
🌌《夜深人静写算法》🌌

一、题目

1、题目描述

  设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
    push(x)—— 将元素 x 推入栈中。
    pop()—— 删除栈顶的元素。
    top()—— 获取栈顶元素。
    getMin()—— 检索栈中的最小元素。

  样例输入: ["MinStack","push","push","push","getMin","pop","top","getMin"]
  样例输出: [null,null,null,null,-3,null,0,-2]

2、基础框架

  • C语言 版本给出的基础框架代码如下:
typedef struct {

} MinStack;

/** initialize your data structure here. */

MinStack* minStackCreate() {

}

void minStackPush(MinStack* obj, int val) {

}

void minStackPop(MinStack* obj) {

}

int minStackTop(MinStack* obj) {

}

int minStackGetMin(MinStack* obj) {

}

void minStackFree(MinStack* obj) {

}

/**
 * Your MinStack struct will be instantiated and called as such:
 * MinStack* obj = minStackCreate();
 * minStackPush(obj, val);
 
 * minStackPop(obj);
 
 * int param_3 = minStackTop(obj);
 
 * int param_4 = minStackGetMin(obj);
 
 * minStackFree(obj);
*/
  • 要求支持一般的栈操作以外,还需要支持一种操作,就是 O ( 1 ) O(1) O(1) 的时间获取最小值。

3、原题链接

( 1 ) (1) (1) LeetCode 155. 最小栈
( 2 ) (2) (2) 面试题 03.02. 栈的最小值
( 3 ) (3) (3) 剑指 Offer 30. 包含min函数的栈

二、解题报告

1、思路分析

1)栈的数据

  • 每个栈的元素用一个结构体表示,除了存储数据本身,还需要存储一个全局 ID,利用这个 ID 可以用于判断两个 栈数据 是否相等。
struct Node {
    int idx;
    int val;
};

2)数据结构设计

  • 对于这个最小栈,设计的时候需要设计两个栈,一个是单调栈,一个是正常的栈,如下:
typedef struct {
    struct Stack normal;
    struct Stack min;
    int idx;
} MinStack;

3)算法思路

  • 利用两个栈,一个是单调栈,一个是正常栈;
  • 正常栈处理所有和 最小值 无关的操作,取 最小值 这个操作就是取的 单调栈 的栈顶;
  • 考虑进栈元素 1、3。 基于 后进先出 这个特点,1 这个元素在栈中的生命周期肯定会被 3 长,且 1 更小,所以,1 比 3 更优,那么基于单调性,如果 1 和 3 依次进入单调栈,3 应该被弹出来(或者说 3 根本就不用进单调栈)。于是,我们发现单调栈的性质,从 栈低栈顶 不可能是单调递增的,所以它就是单调递减的。
  • 再考虑进栈元素1、0, 1 的声明周期虽然比 0 长,但是 0 的值比 1 小,在 0 没有弹栈之前,最小值应该是 0;弹栈之后,最小值才是 1,所以两者都应该被栈保留下来,如下图所示:
  • 于是,思路就清晰了,单调栈对于每一步操作,始终维护一个单调递减的栈即可。
  • 有关单调栈更多的内容,可以看以下这篇文章:夜深人静写算法(十一)- 单调栈

4)接口实现

2、时间复杂度

  • 由于每个括号最多入栈一次,出栈一次。
  • 所以时间复杂度:每一步操作的均摊时间复杂度为 O ( 1 ) O(1) O(1)

3、代码详解

/************************************* 栈的顺序表实现 *************************************/

#define DataType struct Node
#define maxn 100010

struct Node {
    int idx;
    int val;
};

struct Stack {
    DataType data[maxn];
    int top;
};

void StackClear(struct Stack* stk) {
    stk->top = 0;
}
void StackPushStack(struct Stack *stk, DataType dt) {
    stk->data[ stk->top++ ] = dt;
}
void StackPopStack(struct Stack* stk) {
    --stk->top;
}

DataType StackGetTop(struct Stack* stk) {
    return stk->data[ stk->top - 1 ];
}
int StackGetSize(struct Stack* stk) {
    return stk->top;
}
bool StackIsEmpty(struct Stack* stk) {
    return !StackGetSize(stk);
}
/************************************* 栈的顺序表实现 *************************************/

typedef struct {
    struct Stack normal;
    struct Stack min;
    int idx;
} MinStack;

/* (1) */
MinStack* minStackCreate() {
    MinStack *ms = (  MinStack *)malloc( sizeof(MinStack) );
    StackClear( &ms->normal );
    StackClear( &ms->min );
    ms->idx = 0;
    return ms;
}

/* (2) */
void minStackPush(MinStack* obj, int val) {
    
    struct Node nd;
    nd.idx = ++obj->idx;
    nd.val = val;

    if( !StackIsEmpty(&obj->min) ) {
        if( StackGetTop(&obj->min).val > nd.val ) {
            StackPushStack( &obj->min, nd );
        }
    } else {
        StackPushStack( &obj->min, nd );
    }
    StackPushStack( &obj->normal, nd);
}

/* (3) */
void minStackPop(MinStack* obj) {
    struct Node nd    = StackGetTop( &obj->normal );
    struct Node ndmin = StackGetTop( &obj->min );

    if(nd.idx == ndmin.idx)
        StackPopStack(&obj->min);
    StackPopStack(&obj->normal);
}

/* (4) */
int minStackTop(MinStack* obj) {
    return StackGetTop( &obj->normal ).val;
}

/* (5) */
int minStackGetMin(MinStack* obj) {
    return StackGetTop( &obj->min ).val;
}

void minStackFree(MinStack* obj) {
    free(obj);
}

/**
 * Your MinStack struct will be instantiated and called as such:
 * MinStack* obj = minStackCreate();
 * minStackPush(obj, val);
 
 * minStackPop(obj);
 
 * int param_3 = minStackTop(obj);
 
 * int param_4 = minStackGetMin(obj);
 
 * minStackFree(obj);
*/
  • ( 1 ) (1) (1) minStackCreate:利用malloc申请空间后返回内存首地址作为 最小栈 的基地址;
  • ( 2 ) (2) (2) minStackPush:确保 push 操作后,单调栈 是单调递减的,所以如果它比 栈顶元素大 就没必要入栈了;而 正常栈 正常 push 即可;
  • ( 3 ) (3) (3) minStackPop正常栈 的元素是一定会 pop 的,如果 正常栈栈顶元素单调栈栈顶元素 相同,则单调栈 pop;这里相同判定不能判定数值,而是要用之前定义的 全局ID
  • ( 4 ) (4) (4) minStackTop:取栈顶,就是取 正常栈栈顶元素
  • ( 5 ) (5) (5) minStackGetMin:取最小值,就是取 单调栈栈顶元素

三、本题小知识

   我们在设计数据结构的时候,对外提供的可能是一种数据结构,但是内部实现我们往往会对常用数据结构进行组合。即对于外部来说,只需要暴露接口即可,它们并不知道内部是如何实现的,这样就很好的将要做的事情进行了抽象。


以上是关于⭐算法入门⭐《栈 - 单调栈》简单01 —— LeetCode 155. 最小栈的主要内容,如果未能解决你的问题,请参考以下文章

⭐算法入门⭐《栈 - 单调栈》困难02 —— LeetCode 85. 最大矩形

单调栈算法 入门+博客推荐+模板

[算法]单调栈专题

单调栈

830. 单调栈

⭐算法入门⭐《栈 和 队列》简单01 —— LeetCode 232. 用栈实现队列