题目一:实现一个栈,要求实现Push(出栈)、Pop(入栈)、Min(返回最小值)的时间复杂度为O(1)
本题目的GitHub地址
-
本题目的实现思路比较简单,使用两个栈,一个栈是用来存储数字,就是普通的数据,在代码中这个栈为
Stack
-
结构体的设计:数字+出现次数,另一个栈便用来存储结构体,在代码中这个栈为
NumStack
,这个栈的栈顶永远存的是Stack
栈中的最小值和最小值个数构成的结构体 -
所以我们将要设计的这个栈命名为
DoubleStack
,提供入栈,出栈,返回最小值得接口和其它基本接口 -
入栈:首先我们先考虑入栈,入栈的时候呢,对于Stack肯定是必须要入栈的,对于NumStack的话就需要考虑了,如果NumStack是空栈,那么肯定是第一次进入元素,所以两个栈都要存东西,而且对于第一次存入的应该将最小值对应的结构体设置为1
-
如果
NumStack
栈不为空的话,那么在压栈之前就需要比较大小,如果比NumStack
栈栈顶元素还小的话就需要将这个较小值压栈,如果相等的话就对栈顶元素的结构体的num+1即可! -
出栈:首先还是先要获取栈顶元素,因为不是每一次入
Stack
栈的元素都能存入到NumStack
中,所以要出栈的元素和NumStack
的栈顶相等的话就将对应的结构体的count属性-1,要是count原本就是1,那么直接将此元素在NumSatck
中出栈即可! -
上一步只是判断
NumStack
中的元素是否需要出栈,但是将Stack
中的元素出栈是必要的,所以最后将Stack
中的元素弹栈!
GIF演示过程——入栈
GIF演示过程——出栈
void DoubleStackPush(DoubleStack* ds, IDataType x)
{
SDataType s;
SDataType ret;
assert(ds);
//如果结构体栈为空
if(NStackEmpty(&ds->nst) == 0)
{
s.count = 1;
s.num = x;
StackPush(&ds->st,x);
NStackPush(&ds->nst, s);
}
else
{
StackPush(&ds->st, x);
//如果入栈的元素小于最小值记录栈的栈顶
if(x < NStackTop(&ds->nst).num)
{
s.num = x;
s.count = 1;
//将此元素的对应结构体入栈
NStackPush(&ds->nst, s);
}
else if (x == NStackTop(&ds->nst).num)
{
//相等的情况
ret = NStackTop(&ds->nst);
NStackPop(&ds->nst);
ret.count += 1;
NStackPush(&ds->nst, ret);
}
}
}
void DoubleStackPop(DoubleStack* ds)
{
SDataType ret;
assert(ds);
//先获取结构体栈的栈顶元素
ret = NStackTop(&ds->nst);
//如果像个相等就做结构体计数-1或者出栈操作
if(ret.num == StackTop(&ds->st))
{
if(ret.count == 1)
{
NStackPop(&ds->nst);
}
else
{
ret.count--;
NStackPop(&ds->nst);
NStackPush(&ds->nst, ret);
}
}
//将原本存数字出栈
StackPop(&ds->st);
}
题目二:一个数组实现两个栈(共享栈)
本题需要使用一个数组来完成两个栈,于是最容易实现这两个栈的方法就是奇偶栈!
数组中的奇数位用来存储一个栈的数据,偶数位用来存储另一个栈的数据!
#include "ShareStack.h"
void ShareStackInit(ShareStack* pss)
{
assert(pss);
pss->_top1 = 0;
pss->_top2 = 1;
}
//which 1/2
void ShareStackPush(ShareStack* pss, SSDataType x, int which)
{
assert(pss && (which == 1 || which == 2));
if (pss->_top1 < N && pss->_top2 < N)
{
if (which == 1)
{
pss->_a[pss->_top1] = x;
pss->_top1 += 2;
}
else
{
pss->_a[pss->_top2] = x;
pss->_top2 += 2;
}
}
else
printf("空间不足\n");
}
void ShareStackPop(ShareStack* pss, int which)
{
assert(pss && (which == 1 || which == 2));
if (which == 1 && (pss->_top1 - 2 >= 0))
pss->_top1 -= 2;
else if (which == 2 && (pss->_top2 - 2 >= 0))
pss->_top2 -= 2;
}
int error_flag = 0;
SSDataType ShareStackTop(ShareStack* pss, int which)
{
assert(pss && (which == 1 || which == 2));
if (which == 1 && pss->_top1-2 >= 0)
return pss->_a[pss->_top1 -2];
else if (which == 2 && pss->_top2- 2 >= 0)
return pss->_a[pss->_top2 -2];
error_flag = -1;
return error_flag;
}
void TestShareStack()
{
ShareStack ss;
ShareStackInit(&ss);
ShareStackPush(&ss,11, 1);
ShareStackPush(&ss, 22, 1);
ShareStackPush(&ss, 33, 2);
ShareStackPush(&ss, 44, 1);
printf("%d\n", ShareStackTop(&ss, 1));
ShareStackPop(&ss, 1);
printf("%d\n", ShareStackTop(&ss, 1));
ShareStackPop(&ss, 1);
printf("%d\n",ShareStackTop(&ss, 2));
ShareStackPop(&ss, 2);
}
题目三:元素出栈、入栈顺序的合法性。如入栈的序列(1,2,3,4,5),出栈序列为 (4,5,3,2,1) 。
这个问题需要将in
数组不停的入栈将每个入栈的元素与out
比较,相同的就出栈,否则继续入栈,然后直到in
数组已经没有元素可以入栈,接着就是逐个比较剩下的元素,每一个元素都要匹配才可以认定是匹配的!如下图:
#include "Stack.h"
int IsLegal(int *in, int *out, int insize, int outsize)
{
int i = 0;
int j = 0;
Stack st;
//如果指针无效或者长度不一样是无法判断的
assert(in && out && insize == outsize);
StackInit(&st);
//将in数组中的元素压入栈中逐个比较,如果栈顶元素与out数组中的一致就弹出此元素
while(i<insize)
{
StackPush(&st, in[i++]);
if (StackTop(&st) == out[j]) {
StackPop(&st);
j++; //注意out数组中比较过的元素要跳过,所以下标要+1
}
}
//此时栈中剩下的元素要与out数组中剩下的元素逐个比较,遇到不一样的就不匹配
while (StackEmpty(&st) != 0)
{
if(StackTop(&st) != out[j++])
{
return 0;
}
StackPop(&st);
}
//此时为空栈,则说明匹配
return 1;
}
int main(void)
{
int in[5] = {1, 2, 3, 4, 5};
int out[5] = {4, 5, 3, 2, 1 };
int ret = IsLegal(in, out, 5, 5);
if (ret == 1)
printf("匹配\n");
else
printf("不匹配\n");
system("pause");
return 0;
}
测试用例:
入站数据:int in[5] = {1, 2, 3, 4, 5};
两组合法数据:
int out[5] = {4, 5, 3, 2, 1 };
int out[5] = {2, 3, 4, 5, 1 };
两组非法数据:
int out[5] = {4, 5, 3, 1, 2 };
int out[5] = {4, 3, 2, 5, 1 };
题目四:使用两个栈实现一个队列
- 要实现Push接口其实不难,因为我们只需要向一个栈里面Push数据即可,我们暂且将这个栈定义为栈一,另一个栈设定为栈二
- 要实现Pop接口的话,其实只要先判断栈二是否为空栈,如果栈二不为空那么直接Pop出一个元素即可,如果栈二不为空,那么需要先将栈一中的元素全部转移到栈二中,然后栈二就有元素了,直接Pop出一个即可!
#include "Topic.h"
void QueueByTwoStackInit(QueueByTwoStack* qts)
{
assert(qts);
StackInit(&(qts->s1));
StackInit(&(qts->s2));
}
void QueueByTwoStackDestory(QueueByTwoStack* qts)
{
assert(qts);
StackDestory(&(qts->s1));
StackDestory(&(qts->s2));
}
//永远向栈一Push元素
void QueueByTwoStackPush(QueueByTwoStack* qts, DataType x)
{
assert(qts);
StackPush(&(qts->s1),x);
}
void QueueByTwoStackPop(QueueByTwoStack* qts)
{
assert(qts);
//如果栈二为空需要先将栈一中的元素全部转移到栈二中
if (StackEmpty(&(qts->s2)) == 0)
{
while (StackEmpty(&(qts->s1)))
{
StackPush(&(qts->s2), StackTop(&(qts->s1)));
StackPop(&(qts->s1));
}
}
StackPop(&(qts->s2));
}
DataType QueueByTwoStackFront(QueueByTwoStack* qts)
{
assert(qts);
if (StackEmpty(&(qts->s2)) == 0)
{
while (StackEmpty(&(qts->s1)))
{
StackPush(&(qts->s2), StackTop(&(qts->s1)));
StackPop(&(qts->s1));
}
}
return StackTop(&(qts->s2));
}
int QueueByTwoStackSize(QueueByTwoStack* qts)
{
assert(qts);
return StackSize(&(qts->s1)) + StackSize(&(qts->s2));
}
int QueueByTwoStackEmpty(QueueByTwoStack* qts)
{
assert(qts);
return StackEmpty(&(qts->s1)) | StackEmpty(&(qts->s2));
}
void TestQueueByTwoStack()
{
QueueByTwoStack qs;
QueueByTwoStackInit(&qs);
QueueByTwoStackPush(&qs, 11);
QueueByTwoStackPush(&qs, 22);
QueueByTwoStackPush(&qs, 33);
QueueByTwoStackPush(&qs, 44);
QueueByTwoStackPop(&qs);
QueueByTwoStackPop(&qs);
QueueByTwoStackPush(&qs, 55);
QueueByTwoStackDestory(&qs);
}
题目五:使用两个队列实现一个栈
接下来是两个队列实现一个栈,首先先大致说一下怎么实现?
如图所示,两个队列总有一个是空队列,一个是非空队列,要入栈的话,必须要入非空队列,出栈的话,必须让非空队列将元素转移到空队列里面,直到非空队列剩余一个元素,然后将剩余的这一个元素出队列,即可完成“出栈”
#include "motive.h"
//初始化
void StackByTwoQueueInit(StackByTwoQueue* stq)
{
assert(stq);
QueueInit(&(stq->q1));
QueueInit(&(stq->q2));
}
//销毁
void StackByTwoQueueDestory(StackByTwoQueue* stq)
{
assert(stq);
QueueDestory(&(stq->q1));
QueueDestory(&(stq->q2));
}
//分配空队列与非空队列
static EmptyCmp(Queue** em, Queue** noem, StackByTwoQueue** pstq)
{
assert(stq);
QueueEmpty(&(*pstq)->q1) == 1 ? ((*noem = &(*pstq)->q1), *em = &((*pstq)->q2)) : ((*noem = &(*pstq)->q2), *em = &((*pstq)->q1));
}
//获取顶部数据
DataType StackByTwoQueueTop(StackByTwoQueue* stq)
{
assert(stq);
Queue *empty;
Queue *noempty;
DataType ret;
//通过三元运算符确定空队列与非空队列
EmptyCmp(&empty, &noempty, &stq);
//将非空队列的元素转移到非空队列,保留一个
while (QueueSize(noempty) > 1)
{
QueuePush(empty, QueueFront(noempty));
QueuePop(noempty);
}
//获取保留结果数据
ret = QueueFront(noempty);
//将剩下的一个数据也转移
QueuePop(noempty);
QueuePush(empty, ret);
return ret;
}
//判断模拟栈是否为空,为空返回0,不为空返回1
int StackByTwoQueueEmpty(const StackByTwoQueue* stq)
{
assert(stq);
//只要有一个队列不为空那么模拟栈就不为空
return QueueEmpty(&(stq->q1)) | QueueEmpty(&(stq->q2));
}
//模拟栈中存入的元素个数
int StackByTwoQueueSize(const StackByTwoQueue* stq)
{
assert(stq);
return QueueSize(&(stq->q1)) + QueueSize(&(stq->q2));
}
//Push数据
void StackByTwoQueuePush(StackByTwoQueue* stq, DataType x)
{
Queue *empty;
Queue *noempty;
assert(stq);
EmptyCmp(&empty, &noempty, &stq);
//永远向不为空的队列插入数据
QueuePush(noempty,x);
}
//Pop数据
void StackByTwoQueuePop(StackByTwoQueue* stq)
{
Queue *empty;
Queue *noempty;
assert(stq);
EmptyCmp(&empty, &noempty, &stq);
//与StackByTwoQueueTop()一样的过程,只是最后余下的元素直接出队列
while (QueueSize(noempty) > 1)
{
QueuePush(empty, QueueFront(noempty));
QueuePop(noempty);
}
printf("Pop:%d\n", noempty->_front->_data);
QueuePop(noempty);
}
//测试函数
void TestStackByTwoQueue()
{
StackByTwoQueue sq;
StackByTwoQueueInit(&sq);
StackByTwoQueuePush(&sq, 11);
StackByTwoQueuePush(&sq, 22);
StackByTwoQueuePush(&sq, 33);
StackByTwoQueuePush(&sq, 44);
StackByTwoQueuePop(&sq);
StackByTwoQueuePop(&sq);
StackByTwoQueuePush(&sq, 55);
StackByTwoQueuePop(&sq);
StackByTwoQueuePop(&sq);
StackByTwoQueuePop(&sq);
StackByTwoQueueDestory(&sq);
}