栈与队列的性质及区别
栈与队列都是线性结构,栈类似于弹夹,只能在一端(栈顶)插入以及删除数据,而队列类似于现实中排队,只能再一端(队尾)插入数据,另一端(队首)删除数据。
用两个栈实现队列
1.模拟队列的结构
我们可以用两个栈来实现一个队列,方法是一个栈专门用来入数据(入队列),另一个专门出数据(出队列)。
实现之前要确保已经实现好了一个能完成各项基本操作的栈结构:
typedef int StackDataType;
typedef struct Stack {
StackDataType* arr;
size_t top;
size_t capacity;
}Stack;
void StackInit(Stack* ps);//初始化
void StackDestory(Stack* ps);//销毁
void StackPush(Stack* ps, StackDataType x);//压栈
void StackPop(Stack* ps);//出栈
bool StackEmpty(Stack* ps);//判断是否为空栈
StackDataType StackTop(Stack* ps);//访问栈顶元素
size_t StackSize(Stack* ps);//获取栈的大小
则模拟的队列结构如下:
typedef struct {
Stack PushStack;
Stack Poptack;
} MyQueue;
2.实现队列的基础操作
操作清单:
MyQueue* Create() 创建一个队列返回它的地址
void push(MyQueue* obj,int x) 将元素 x 推到队列的末尾
int pop(MyQueue* obj) 从队列的开头移除并返回元素
int peek(MyQueue* obj) 返回队列开头的元素
bool empty(MyQueue* obj) 如果队列为空,返回 true ;否则,返回 false
1.初始化
MyQueue* myQueueCreate() {
MyQueue*newqueue=malloc(sizeof(MyQueue));
StackInit(&newqueue->PuStack);
StackInit(&newqueue->PoStack);
return newqueue;
}
创建一个模拟队列,将其中的两个栈分别初始化后返回模拟队列的指针。
2. 入队列
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->PuStack,x);
}
将数据压入专门用于入数据的栈。
3.出队列
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->PoStack)){
while(!StackEmpty(&obj->PuStack)){
StackPush(&obj->PoStack,StackTop(&obj->PuStack));
StackPop(&obj->PuStack);
}
}
int ret=StackTop(&obj->PoStack);
StackPop(&obj->PoStack);
return ret;
}
队列出数据相当于是出入数据的栈中栈底的元素,而栈却只能在栈顶删除数据,所以我们只需要将入数据的栈中每一个数据都倒入专门出数据的栈,原本入数据栈中 栈底的元素就变为了出数据栈中的栈顶元素,故只要当出数据栈的栈顶不为空,我们依次出栈就满足队列的“先进先出”原则,若为空则将入数据栈中的元素都倒过来即可。
4.访问队头元素
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->PoStack)){
while(!StackEmpty(&obj->PuStack)){
StackPush(&obj->PoStack,StackTop(&obj->PuStack));
StackPop(&obj->PuStack);
}
}
return StackTop(&obj->PoStack);
}
对头元素就是队列中第一个待出的元素。
5.检查队列是否为空
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->PoStack)&&StackEmpty(&obj->PuStack);
}
6.销毁队列
void myQueueFree(MyQueue* obj) {
StackDestory(&obj->PoStack);
StackDestory(&obj->PuStack);
}