3.2 How would you design a stack which, in addition to push and pop,also has a function min which returns the minimum element? Push, popand min should all operate in O(1) time.
译文:实现一个栈,除了 push和 pop操作,还要实现一个 min函数返回栈中的最小值,push,pop,min的时间复杂度都为O(1)。
这里的问题是返回栈中的最小值的时间复杂度要求为O(1),这样 min 函数的实现就不能通过每次遍历栈空间来寻找最小值,可以想到通过一个变量来保存当前栈中的最小值,每次压栈时将其与待压入元素作比较,替换。但是出栈时,如果出栈元素恰为最小值,那么这个最小值变量便无法得到更新,所以一个变量是不行的。可以在数据节点中添加一个变量,时刻记录该栈中的最小值,所以只需要在压栈的时候更新最小值,而不必在出栈时更新。
const int Size = 1024;
/*
节点记录当前值和栈底到该节点间的最小值
也就是说每个数据节点都保存从栈底到该位置的数据的最小值
*/
typedef struct Node
{
int val, min;
}Node;
void push(Node buf[], int val, int *cur)
{
buf[++(*cur)].val = val;
if (val < buf[*cur - 1].min)
buf[*cur].min = val;
else
buf[*cur].min = buf[*cur - 1].min;
}
void pop(Node buf[], int *cur)
{
if (0 == *cur)
return;
--(*cur); //修改位置即可
}
int min(Node buf[], int cur)
{
return buf[cur].min;
}
程序改为C++版本,更清晰。
const int MinInt = ~(1 << 31);
typedef struct Node
{
int val, min;
}Node;
class Stack
{
public:
Stack(int size = 1024)
{
buf = new Node[size];
buf[0].min = MinInt;
cur = 0;
}
~Stack()
{
delete [] buf;
}
void push(int val)
{
buf[++cur].val = val;
if (val < buf[cur - 1].min)
buf[cur].min = val;
else
buf[cur].min = buf[cur - 1].min;
}
void pop()
{
--cur;
}
int min()
{
return buf[cur].min;
}
private:
Node *buf;
int cur;
};
上面有那么两个问题,一是如果我们有一个很大的栈,我们就必须浪费很大的空间去保存那个记录栈底至当前栈顶的最小值的变量,每压入一个数据,就额外多了一个变量;二是在前面那点的基础上,如果栈中每个节点中的数据都是最小值或是相当一部分的节点保存的最小值是相同的,这样就耗费一部分空间去保存那个重复的数据,造成空间的进一步浪费。
我们可以把这个记录局部最小值(栈底至某一位置的最小值)的变量从节点中分离出来,这样当局部最小值是同一值时,我们就可以只保存一份,而不必重复的保存等数据个数的份。当然并不是简单的分离,这里采用两个栈来实现,其中一个栈 s1保存数据,另一个栈 s2则保存局部最小值,就是栈 s1中栈底到某一位置的局部最小值,这个位置取决于栈 s1该位置的数据是否是局部最小值。
const int MinInt = ~(1 << 31);
class stack
{
public:
stack(int size = 1024)
{
buf = new int[size];
cur = -1;
}
~stack()
{
delete[] buf;
}
void push(int val)
{
buf[++cur] = val;
}
void pop()
{
--cur;
}
int top()
{
return buf[cur];
}
bool isempty()
{
return cur == -1;
}
/*测试代码*/
void print()
{
while (-1 != cur)
{
cout << top() << endl;
pop();
}
}
private:
int *buf;
int cur;
};
class StackwithMin
{
public:
StackwithMin() {}
~StackwithMin() {}
int min() //s2记录局部最小值
{
if (s2.isempty())
return MinInt;
else
return s2.top();
}
void push(int val)
{
s1.push(val);
if (val <= min()) // < 会导致pop掉s2的最小值数据
s2.push(val); //压栈的数据小于局部最小值,则将该数据值压入s2作为新的局部最小值
}
void pop()
{
if (s1.top() == min()) //如果弹出的数据正好为局部最小值,则s2也弹出
s2.pop();
s1.pop();
}
/*测试代码*/
void print()
{
cout << "s1: top to bottom" << endl;
s1.print();
cout << endl;
cout << "s2: top to bottom" << endl;
s2.print();
}
private:
stack s1, s2;
};
上面程序对空间的占用有了较大的改善,但是也有一点缺陷如果压入的数据全部为相同值或连续压入的数据恰好等于局部最小值,那么会在栈s2中保存重复的值,造成部分空间浪费。这样可考虑额外增加空间来记录s1中该值为局部最小值的次数,有时间再来解决吧 >_< !