栈和队列面试题:
- 实现一个栈,要求实现Push(出栈)、Pop(入栈)、Min(返回最小值)的时间复杂度为O(1)
- 使用两个栈实现一个队列
- 使用两个队列实现一个栈
- 元素出栈、入栈顺序的合法性。如入栈的序列(1,2,3,4,5),出栈序列为 (4,5,3,2,1)
- 一个数组实现两个栈(共享栈)
实现一个栈,要求实现Push(出栈)、Pop(入栈)、Min(返回最小值)的时间复杂度为O(1)
问题分析:栈的入栈操作和出栈操作时间复杂度本来就是O(1),所以这道题目的关键点在于如何处理才能做到返回栈中最小值时的时间复杂度也为O(1)
方法一:用一个栈
入栈:第一个元素入栈时(当前栈为空),push 两次第一个数据,第一次 push 的数据代表当前入栈的数据,第二次 push 的数据代表当前栈中最小的元素;第二个元素入栈时,先将该数据与当前栈顶的数据进行比较并记录下二者当中较小的数据,然后 push 第二个数据,接个 push 比较后记录的较小的那个数据(该数据就是当前栈中的最小值);后面入栈的数据做同样的处理
出栈:出栈时需 pop 两次,第一次 pop 的结果是当前栈中最小的元素,第二次 pop 的结果是真真意义上的栈顶元素
取最小值:当前栈顶的元素就是栈中所有元素的最小值
说明:方式一下的栈接口与普通的栈接口一模一样,这里的代码前一篇博客已经实现了,可以参考:数据结构—栈
方式二:用两个栈
入栈:第一个元素入栈时(栈为空),栈 S1和S2 中都 push 第一个数据,栈S1中的数据代表当前入栈的数据,栈S2中的数据代表当前栈中最小的数据;第二个数据入栈时(假设栈不为空),栈S1中直接 push 第二个数据,然后比较第二个数据和S2栈顶的数据并记录下较小的那个数据,将记录下的较小值 push 到S2中作为当前栈中最小的数据;后面入栈的数据做同样的处理
出栈:同时 pop 栈S1和S2,栈S1进行 pop 的结果是删除当前栈顶的元素,栈S2进行 pop 的结果是更新了栈中的最小元素记录
取最小值:栈S2栈顶的元素就是当前所有元素中的最小值
结构的定义:
typedef struct MinStack //最小值栈
{
Stack _s1; //存数据
Stack _s2; //存当前栈中数据的最小值
}MinStack;
基本接口实现:
void MinStackInit(MinStack* ms)
{
assert(ms);
StackInit(&ms->_s1);
StackInit(&ms->_s2);
}
void MinStackPush(MinStack* ms,DataType x)
{
assert(ms);
if (StackEmpty(&ms->_s1) == 0) //说明栈为空,将第一个数据直接 push 到两个栈中
{
StackPush(&ms->_s1, x);
StackPush(&ms->_s2, x);
return; //这里的 return 千万不能忘
}
//走到这里说明栈中已经有了数据
DataType min = StackTop(&ms->_s2) < x ? StackTop(&ms->_s2) : x;
StackPush(&ms->_s1, x);
StackPush(&ms->_s2, min);
}
void MinStackPop(MinStack* ms) //出栈
{
assert(ms);
StackPop(&ms->_s1);
StackPop(&ms->_s2);
}
DataType MinStackTop(MinStack* ms) //栈顶元素
{
assert(ms);
return StackTop(&ms->_s1);
}
DataType MinStackMinDate(MinStack* ms) //查看当前栈中的最小元素
{
assert(ms);
return StackTop(&ms->_s2);
}
size_t MinStackSize(MinStack* ms) //求栈中元素个数
{
assert(ms);
return StackSize(&ms->_s1);
}
int MinStackEmpty(MinStack* ms) //判断栈是否为空
{
assert(ms);
return StackEmpty(&ms->_s1);
}
使用两个栈实现一个队列
问题说明:用两个栈实现队列的过程中必须要保证栈特征,即先进后出的原则,只是对外表现出队列的特性罢了
结构的定义:
typedef struct QueueByStack //队列
{
Stack _s1;
Stack _s2;
}QueueByStack;
基本接口实现:
void QueueByStackInit(QueueByStack* q) //队的初始化
{
assert(q);
StackInit(&q->_s1);
StackInit(&q->_s2);
}
void QueueByStackPush(QueueByStack* q,DataType x) //入队
{
assert(q);
StackPush(&q->_s1,x);
}
void QueueByStackPop(QueueByStack* q) //出队
{
assert(q);
if (StackEmpty(&q->_s2) == 0) //S2为空
{
if (StackEmpty(&q->_s1) == 0) //S1也是空,说明队列无数据可删
return;
while (StackEmpty(&q->_s1)) { //将S1中的所有数据push到S2
StackPush(&q->_s2,StackTop(&q->_s2));
StackPop(&q->_s1);
}
}
//走到这里说明S2不为空,直接删就行
StackPop(&q->_s2);
}
size_t QueueByStackSize(QueueByStack* q) //求队列的元素个数
{
assert(q);
return StackSize(&q->_s1)+StackSize(&q->_s2); //两个栈中元素的总和才是队列的元素个数
}
int QueueByStackEmpty(QueueByStack* q)
{
assert(q);
return StackEmpty(&q->_s1) + StackEmpty(&q->_s2); //这里两个栈都要做判断
}
使用两个队列实现一个栈
结构的定义:
typedef struct StackByQueue //栈
{
Queue _q1;
Queue _q2;
}StackByQueue;
基本操作接口实现: