目录
栈和队列的比较
队列和栈是两种不同的数据结构。它们有以下区别:
(1)操作的名称不同。队列的插入称为入队,队列的删除称为出队。栈的插入称为进栈,栈的删除称为出栈。
(2)可操作的方式不同。队列是在队尾入队,队头出队,即两边都可操作。而栈的进栈和出栈都是在栈顶进行的,无法对栈底直接进行操作。
(3)操作的方法不同。队列是先进先出(FIFO),即队列的修改是依先进先出的原则进行的。新来的成员总是加入队尾(不能从中间插入),每次离开的成员总是队列头上(不允许中途离队)。而栈为后进先出(LIFO),即每次删除(出栈)的总是当前栈中最新的元素,即最后插入(进栈)的元素,而最先插入的被放在栈的底部,要到最后才能删除。
但是他们两也有一个共同点就是只允许在端点处插入和删除元素。
所以两者可以相互实现,也就是栈实现队列,队列实现栈。
栈实现队列
leetcode链接:栈实现队列
栈实现队列的思想就是通过两个栈,一个入值栈,一个出值栈,这样就可以实现队尾入队头出,先进先出的原则。
然后将pushST的值全部送到popST中
最后就是popST的出栈
这样就可以通过两个栈来实现队列了。
代码实现
解析内容已经在代码的注释中给出。
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int capacity;
int top;
}ST;
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
void StackPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
printf("realloc failed\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void StackPop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
STDataType StackTop(ST* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top - 1];
}
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
//以上内容是栈的基本实现,之前文章中已经提到。
typedef struct {
ST pushST;
ST popST;
} MyQueue; //这里定义两个栈,包含在结构体Queue中
//我的队列的创建,也就是pushST和popST的创建
MyQueue* myQueueCreate() {
MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));//malloc出空间
StackInit(&q->pushST);//初始化pushST栈
StackInit(&q->popST); //初始化popST栈
return q;
}
//入队列也就是入值到pushST中,通过入栈操作实现
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->pushST,x);
}
//从队列的开头移除并返回元素
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->popST)) //先判断是否为空
{
//如果popST为空的话,需要先将pushST的值全部入到popST。
while(!StackEmpty(&obj->pushST))
{
//pushST到popST也是后者进入一个元素,前者删除一个元素,循环操作直到pushST空
StackPush(&obj->popST,StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
//将popST的值赋给front,然后再将其通过出栈操作出栈,最后再返回front的值
int front = StackTop(&obj->popST);
StackPop(&obj->popST);
return front;//front的值就是队头的元素
}
// 返回队列开头的元素
int myQueuePeek(MyQueue* obj) {
//先判断popsT是否尾空
if(StackEmpty(&obj->popST))
{
while(!StackEmpty(&obj->pushST))
{
//和前面的操作一样,将pushST的值都给到popST中
StackPush(&obj->popST,StackTop(&obj->pushST));
StackPop(&obj->pushST);
}
}
//直接找到队头元素也是popST的栈顶元素,直接返回这个值即可
return StackTop(&obj->popST);
}
//如果队列为空,返回 true ;否则,返回 false
bool myQueueEmpty(MyQueue* obj) {
//返回popST和pushST都为空的命题的真假,如果真就是返回true,假就返回false
return StackEmpty(&obj->popST)&&StackEmpty(&obj->pushST);
}
//我的队列的销毁,也就是两个栈的销毁
void myQueueFree(MyQueue* obj) {
//需要先销毁两个栈再去释放obj指针指向的空间
StackDestroy(&obj->popST);
StackDestroy(&obj->pushST);
free(obj);
}
队列实现栈
队列实现栈思想是通过两个队列来实现。入值时,往不为空的队列入,保持着另一个队列为空,出值时,总共n个数据,先将前面的n-1个数据转移到另一个队列,原来的队列就只剩下一个数据了,再将这个数据出队列就实现了先进后出,后进先出的操作。入值时需要入到有值的队列中去。
leetcode链接:225. 用队列实现栈 - 力扣(LeetCode)
将前面的n-1个元素转移到另一个队列
最后再将之前队列中唯一的元素出队列。
这样就实现了栈的基本性质。
代码实现
解析内容已经在代码的注释中给出。
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QueueNode;
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
}Queue;
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head==NULL;
}
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur != NULL)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
newnode->data = x;
newnode->next = NULL;
if (pq->head == NULL)
{
pq->tail = pq->head = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
if (pq->head == NULL)
{
pq->tail == NULL;
}
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
int QueueSize(Queue* pq)
{
assert(pq);
int count = 0;
QueueNode* cur = pq->head;
while (cur != NULL)
{
count++;
cur = cur->next;
}
return count;
}
//以上内容是对于的队列的实现,前面的文章也有提到
//定义结构体中有两个队列
typedef struct {
Queue q1;
Queue q2;
} MyStack;
我的栈的创建,相当于两个队列的创建
MyStack* myStackCreate() {
MyStack* st = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&st->q1);//初始化队列1
QueueInit(&st->q2);//初始化队列2
return st;
}
// 将元素 x 压入栈顶
void myStackPush(MyStack* obj, int x) {
assert(obj);
//如果队列1不为空就入队列1,否则入队列2,当两个都为空时就是入队列2
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
//移除并返回栈顶元素
int myStackPop(MyStack* obj) {
assert(obj);
//先假设队列1是空,创建结构体指针emptyQ指向q1,nonempty2指向q2
Queue* emptyQ = &obj->q1;
Queue* nonemptyQ = &obj->q2;
//当前面的假设错误时,将两个指针所指向的内容互换
if(!QueueEmpty(&obj->q1))
{
emptyQ = &obj->q2;
nonemptyQ = &obj->q1;
}
//循环将不为空的队列的元素转移到另一个队列,直到剩下一个元素
while(QueueSize(nonemptyQ)>1)
{
//先是空队列的入队列,再是非空队列的出队列,通过前面的队列操作实现
QueuePush(emptyQ,QueueFront(nonemptyQ));
QueuePop(nonemptyQ);
}
//将队首元素给到front,也就是代表着栈的栈顶元素
int top = QueueFront(nonemptyQ);
QueuePop(nonemptyQ); //通过队列操作将这个元素去除
return top;//返回top也就是栈顶元素
}
//返回栈顶元素
int myStackTop(MyStack* obj) {
assert(obj);
//判断队列1是否为空,非空就返回它的队尾元素
//因为两个队列必须会有一个是空的,所以可以直接用if来判断倒是选择哪一个队列的队尾元素
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
//如果栈是空的,返回 true ;否则,返回 false
bool myStackEmpty(MyStack* obj) {
assert(obj);
//返回队列1和队列2都是空的命题的真假,如果是真就返回true,否则就是false
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
//创建的栈的销毁
void myStackFree(MyStack* obj) {
assert(obj);
//销毁栈也就是销毁两个队列
QueueDestroy(&obj->q1);//释放队列1空间
QueueDestroy(&obj->q2);//释放队列2空间
free(obj);//释放obj指针所指向的空间
obj = NULL;
}