定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
题目不难,关键需要一个辅助内存去维护当前栈中的最小元素。第一感觉,就是用用一个multiset去维护最小值列表,不用set的原因是无法插入两个相同的值,但是,测试发现结果不对。错误的第一版代码如下:
class Solution {
public:
void push(int value) {
m_stack.push_back(value);
m_orderSet.insert(value);
}
void pop() {
if (m_stack.empty())
return;
m_orderSet.erase(this->top());
m_stack.erase((m_stack.end() - 1));
}
int top() {
return m_stack.empty() ? 0 : *(m_stack.rbegin());
}
int min() {
return m_orderSet.empty() ? 0 : *(m_orderSet.begin());
}
std::vector<int> m_stack;
std::multiset<int> m_orderSet;
};
一脸懵逼,觉得逻辑没人任何毛病,为啥死活通不过去??最后debug下来,发现问题出现在m_orderSet.erase(this->top())上面,哎,竟然一直没注意到multiset的erase如果传值的话,是会把set内等于该数值的全部元素都会删除掉的。。。
跑去看了下STLPort源码验证下。
set和multiset底层都是红黑树,看RBTree的erase实现:
size_type erase(const key_type& __x) {
pair<iterator,iterator> __p = equal_range(__x);
size_type __n = _STLP_STD::distance(__p.first, __p.second);
erase(__p.first, __p.second);
return __n;
}
pair<iterator,iterator> equal_range(const _KT& __x)
{ return pair<iterator, iterator>(lower_bound(__x), upper_bound(__x)); }
源码清晰可见,RBTree在erase时会先调用equal_range去查找等于这个值的所有元素,并返回这个区间,然后全部删除,最后erase函数会返回这个区间的距离(也就是实际删除的元素个数)。
知道了问题,就好办了,修改pop()部分代码,换成传入迭代器的方式调用erase即可。
for (auto it = m_orderSet.begin(); it != m_orderSet.end(); ++it)
{
if (*it == this->top())
{
m_orderSet.erase(it);
break;
}
}
修改完,提交,测试通过。
最后,再记录分享下另一种实现,采用另一个辅助栈去实现,两个栈,一个用于实际的逻辑栈A,另一个栈B维护当前最小值。
具体操作流程:每push一个元素X到A时,判断下X是否小于B的栈顶元素,如果满足,同时push x到B中,否则,push B的栈顶元素到B中;每次pop时,A、B栈同时pop。
例如 push A: 3, 4, 2, 5, 1; B: 3, 3, 2, 2, 1。