首页 > 代码库 > 【算法】最小栈

【算法】最小栈

实现一个栈,带有出栈(pop)、入栈(push)、取最小元素(getMin)三个方法,且时间复杂度均为O(1)。

初始想法:

  1. 创建 int min = -1,记录栈中最小元素的下标;
  2. 第一个元素进栈时,min = 0;
  3. 每当新元素进栈,让新元素与 min 下标位置的元素比较大小,min = 较小元素的下标;
  4. 调用 getMin 直接返回 min 的值。

这种方式进栈没有问题,而出栈时,若当前最小元素在栈顶并出栈了,用剩下哪个元素的下标顶替当前 min 就不得而知了。所以一旦最小元素的下标出栈,需要次小元素的下标作为备胎顶替,后几轮若次小元素的下标出栈,也需要一个备胎。。。

因此用一个额外的栈存储所有曾经的最小值的下标即可:

  1. 原有栈叫栈A,创建一个额外的栈B;
  2. 第一个元素进入栈A,这个唯一的元素是栈A的当前最小值,其下标就进入栈B;
  3. 每当新元素进入栈A,让新元素与栈B栈顶下标对应的栈A的当前最小值比较,若小于A的当前最小值,则新元素的下标进入栈B;
  4. 每当栈A有元素出栈,若出栈元素的下标 == 栈B的栈顶元素,则B的栈顶也出栈。此时栈B余下的栈顶元素所指向的,是栈A中原本的次小元素,这个第二小的元素就代替了刚出栈的最小元素成为了栈A当前的最小元素;
  5. 调用 getMin 方法直接返回栈B的栈顶元素即可。

这种方法最坏条件的空间复杂度是O(n)。

 

这种额外创建一个栈不断地存储“备胎”,并让“备胎”不断“转正”的思路,让我想到了做 Valid Parentheses 这题时最开始想到的死办法的解决方法。

原理:

    top 表示当前栈顶,从0开始向后扫;i 表示即将入栈的元素,从1开始向后扫;创建额外栈 stack 存储所有上一次的栈顶位置,初始为 -1。这里需要注意的是,若上一次栈顶是 -1,那么 top 下面要到 i + 1 的位置继续扫;若不是 -1,则 top 退到上一次栈顶的位置即可。一开始我没想到用栈存储上一个栈顶下标,仅仅用一个变量存储,结果这种方法没有实现。看到了最小栈后,灵光一现想到了这样实现,时间和空间复杂度和原来的做法一样,都为O(n),不过没有那种做法直观,但也不失为一种思路。

 1 bool isValid(string s) {
 2     stack<int> stack;
 3     int top = 0, i = 1;
 4     stack.push(-1);
 5     while (i < s.size()) {
 6         if ((s[top] == ( && s[i] == )) || (s[top] == [ && s[i] == ]) || (s[top] == { && s[i] == })) {
 7             if (stack.top() == -1) {
 8                 top = i + 1;
 9                 i = top + 1;
10             } else {
11                 top = stack.top();
12                 stack.pop();
13                 i++;
14             }
15         } else {
16             stack.push(top);
17             top = i++;
18         }
19     }
20     return stack.top() == -1 && top == s.size();
21 }

 

【算法】最小栈