一.用队列实现栈
栈的特点就是后进先出,满足这一要求的结构就能被称作栈,至于怎么实现的就是另一回事,反正我将函数接口给你,你只管用就行。一般情况下,我们使用顺序结构来实现栈,原因是栈只需要对一端进行操作,例如入栈,出栈,取栈顶元素等都是在栈顶进行操作。而顺序结构对尾部的插入删除效率还是蛮高的,故我们可将数组的头部当作栈底,尾部当作栈顶,这种实现方式应当是最简单高效的了。
- 入栈:将数据插入不为空的哪一个队列中,若两个队列均为空,插入任意一个都行
- 出栈:将非空队列的数据导入到另一个队列中,直到只剩下一个元素,该元素即为要出栈的元素,pop掉即可
- 判栈空:若两个队列均为空,则栈空
- 返回栈顶元素:取非空队列的队尾元素即可
代码如下(省略了队列的实现)
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* obj = malloc(sizeof(MyStack));
QueueInit(&obj->q1);
QueueInit(&obj->q2);
return obj;
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
void myStackPush(MyStack* obj, int x) {
assert(obj);
//非空队列push
//都为空随便哪个都行
Queue* pNonEmptyQ = &obj->q1;
Queue* pEmptyQ = &obj->q2;
if (QueueEmpty(&obj->q1))
{
pEmptyQ = &obj->q1;
pNonEmptyQ = &obj->q2;
}
QueuePush(pNonEmptyQ, x);
}
int myStackPop(MyStack* obj) {
assert(obj);
assert(!myStackEmpty(obj));
Queue* pNonEmptyQ = &obj->q1;
Queue* pEmptyQ = &obj->q2;
if (QueueEmpty(&obj->q1))
{
pEmptyQ = &obj->q1;
pNonEmptyQ = &obj->q2;
}
//导数据到空队列直到只剩一个元素
while (QueueSize(pNonEmptyQ) > 1)
{
int front = QueueFront(pNonEmptyQ);
QueuePush(pEmptyQ, front);
QueuePop(pNonEmptyQ);
}
int top = QueueFront(pNonEmptyQ);
QueuePop(pNonEmptyQ);
return top;
}
int myStackTop(MyStack* obj) {
assert(obj);
assert(!myStackEmpty(obj));
if (!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
二.用栈实现队列
队列的特点是先进先出,常规的队列是用链式结构来实现的。因为队列要对两端进行操作(入队和出队),而顺序表对尾部插入删除效率很高,但是对头部操作时要大量地移动数据,非常麻烦。所以采用链表来实现队列比较合理,链表的头插和尾插效率都很高,同时为了避免频繁地找尾,于是设置一个尾指针rear。
设置两个栈,一个用于push,另一个用于pop
入队:将数据插入用于push的栈
出队:将数据从用于pop的栈中弹出来,若pop栈为空,就将push栈中的数据导入进来再操作
判队空:若两个栈都为空,则队已空。
取队头:操作和出队类似
代码如下(省略栈的实现)
MyQueue* myQueueCreate() {
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
STInit(&obj->pushst);
STInit(&obj->popst);
return obj;
}
bool myQueueEmpty(MyQueue* obj) {
return STEmpty(&obj->popst) && STEmpty(&obj->pushst);
}
void myQueuePush(MyQueue* obj, int x) {
assert(obj);
STPush(&obj->pushst, x);
}
int myQueuePeek(MyQueue* obj) {
assert(!myQueueEmpty(obj));
if (STEmpty(&obj->popst))
{
//将pushst数据全部导到popst
while (!STEmpty(&obj->pushst))
{
int top = STTop(&obj->pushst);
STPop(&obj->pushst);
STPush(&obj->popst, top);
}
}
int top = STTop(&obj->popst);
return top;
}
int myQueuePop(MyQueue* obj) {
int top = myQueuePeek(obj);
STPop(&obj->popst);
return top;
}
void myQueueFree(MyQueue* obj) {
STDestroy(&obj->pushst);
STDestroy(&obj->popst);
free(obj);
}
三.设计循环队列
本题采用数组来实现循环队列。
如上图所示,假如这个循环队列最多只能存5个数据,我就开辟一个空间为6的数组,其中一个不存放数据。为什么要这样做呢?是为了区分队空与队满的情况。
代码实现:
typedef struct {
int* a;
int k;//最多元素个数
int front;
int rear;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a = (int*)malloc(sizeof(int)*(k + 1));
obj->front = obj->rear = 0;
obj->k = k;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
assert(obj);
return obj->front == obj->rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
assert(obj);
return (obj->rear + 1) % (obj->k + 1) == obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
assert(obj);
if (myCircularQueueIsFull(obj))
{
return false;
}
obj->a[obj->rear] = value;
obj->rear = (obj->rear + 1) % (obj->k + 1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
assert(obj);
if (myCircularQueueIsEmpty(obj))
{
return false;
}
obj->front = (obj->front + 1) % (obj->k + 1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
assert(obj);
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
assert(obj);
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}