设计包含min函数的栈

题目:

设计一个栈,使得PUSH、POP以及GetMin(获取栈中最小元素)能够在常数时间内完成。

 

分析:

很刚开始很容易想到一个方法,那就是额外建立一个最小堆保存所有元素,这样每次获取最小元素只需要O(1)的时间。但是这样的话,PUSH和POP操作就需要O(lgn)的时间了(假定栈中元素个数为n),不符合题目的要求。

那么用1个辅助栈如何呢?

解法:

使用一个辅助栈来保存最小元素,这个解法简单不失优雅。设该辅助栈名字为minimum stack,其栈顶元素为当前栈中的最小元素。这意味着

 

  • 要获取当前栈中最小元素,只需要返回minimum stack的栈顶元素即可。
  • 每次执行push操作,检查push的元素是否小于或等于minimum stack栈顶元素。如果是,则也push该元素到minimum stack中。
  • 当执行pop操作的时候,检查pop的元素是否与当前最小值相等。如果相同,则需要将改元素从minimum stack中pop出去。

代码:

struct StackGetMin {
  void push(int x) {
    elements.push(x);
    if (minStack.empty() || x <= minStack.top()) //push的元素小于当前minStack的最小元素,则push到minStack中
      minStack.push(x);
  }
  bool pop() {
    if (elements.empty()) return false;
    if (elements.top() == minStack.top()) //如果原始栈栈顶元素与minStack栈顶元素相同,则将该元素也从minStack中pop出去。
      minStack.pop();
    elements.pop();
    return true;
  }
  bool getMin(int &min) {
    if (minStack.empty()) {
      return false;
    } else {
      min = minStack.top();
      return true;
    }
  }
  stack<int> elements;
  stack<int> minStack;
};

 

实例:

假定有元素3, 2, 5, 4, 2, 1依次入栈,则原始栈中元素为(1), 辅助栈中元素为(2)

 1                              

 2                              

 4                             1

 5                             2

 2                             2

 3                             3

(1)                  (2)

这样,第1次pop时,1从两个栈都pop出去;第2次pop时,2从两个栈都pop出去;第3次pop,元素4从原始栈pop出去,辅助栈不用pop;第4次pop,元素5从原始栈pop出去,辅助栈不需pop;第5次pop,元素2从两个栈pop出去;第6次pop,元素3从两个栈都pop出去。我们可以发现,每次push或者pop后,辅助栈的栈顶元素总是当前栈的最小元素。

 

 

另一解法(不用辅助栈)

另外一种解法利用存储差值而不需要辅助栈,方法比较巧妙。其中需要说明的几点:

push(int elem)函数在栈中压入当前元素与当前栈中最小元素的差值,然后通过比较当前元素与当前栈中最小元素大小,并将它们中间的较小值压入。

pop()函数执行的时候,先pop出栈顶的两个值,这两个值分别是当前栈中最小值min和最后压入的元素与栈中最小值的差值diff。如果diff<0,则表示最后压入栈的元素是最小的元素,因此只需将min-diff压入栈中,并将min值返回即可。min-diff就是当前元素弹出后,栈中剩下元素的最小值。而如果diff>=0且栈不为空,则表示当前值不是最小值,所以需要在栈中压入最小值min并将diff+min返回;如果栈为空,则表示已经是最后一个数字,直接返回min即可。

stack<int> s;
void push(int elem)
 {
	if (s.empty()) {
		s.push(elem);
		s.push(elem);
	} else {
		int min = s.top();
		s.pop();
		s.push(elem - min);
		s.push(elem < min ? elem : min);
	}
}

int pop() 
{
	int min = s.top();
	s.pop();
	int diff = s.top();
	s.pop();
	if (diff < 0) {
		s.push(min - diff);
		return min;
	} else {
		if (!s.empty()) {
			s.push(min);
			return diff + min;
		}
		return min;
	}
}

int min() 
{
	int min = s.top();
	return min;
}


一个实例如下:

clear(): [ ] 
push(3): [3 3] 
push(4): [3 1 3] 
push(2): [3 1 -1 2] 
push(5): [3 1 -1 3 2] 
push(1): [3 1 -1 3 -1 1] 
push(1): [3 1 -1 3 -1 0 1] 
push(6): [3 1 -1 3 -1 0 5 1] 
push(7): [3 1 -1 3 -1 0 5 6 1]

min() --> 1; pop() --> 7: [3 1 -1 3 -1 0 5 1] 
min() --> 1; pop() --> 6: [3 1 -1 3 -1 0 1] 
min() --> 1; pop() --> 1: [3 1 -1 3 -1 1] 
min() --> 1; pop() --> 1: [3 1 -1 3 2] 
min() --> 2; pop() --> 5: [3 1 -1 2] 
min() --> 2; pop() --> 2: [3 1 3] 
min() --> 3; pop() --> 4: [3 3] 
min() --> 3; pop() --> 3: [ ]


参考资料:

设计包含min的栈另解


 

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值