每日一题---OJ题: 用栈实现队列

片头

嗨! 小伙伴们! 好久不见啦,今天我们来看看这道OJ题---用栈实现队列,准备好了吗? 我们开始咯!

emmm,在上一篇中,我们讲解了怎么用2个队列实现栈,那这一次呢? 那我们就用2个栈实现一个队列吧 ! 

思路一: 定义2个栈,分别命名为Pushst和Popst,命名为Pushst的栈专门用来存储元素, 命名为Popst的栈专门用来移除栈顶元素。

这里,我先把有关栈的代码放出来哈~

栈的头文件 Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int ElemType;
typedef struct Stack {
	ElemType* arr;
	int top;
	int capacity;
}Stack;

//初始化栈
void StackInit(Stack* st);
//入栈
void StackPush(Stack* st, ElemType data);
//出栈
void StackPop(Stack* st);
//获取栈顶元素
ElemType StackTop(Stack* st);
//获取栈中有效元素的个数
int StackSize(Stack* st);
//检测栈是否为空
int StackEmpty(Stack* st);
//销毁栈
void StackDestroy(Stack* st);

栈的具体操作 Stack.c

#include"Stack.h"
//初始化栈
void StackInit(Stack* st) {
	assert(st);
	st->arr = NULL;
	st->top = 0;
	st->capacity = 0;

}
//入栈
void StackPush(Stack* st, ElemType data) {
	assert(st);
	//扩容
	if (st->capacity == st->top) {
		int newCapacity = st->capacity == 0 ? 4 : 2 * (st->capacity);
		ElemType* temp = realloc(st->arr, newCapacity * sizeof(ElemType));
		if (temp == NULL) {
			perror("realloc fail!\n");
			exit(1);
		}
		st->arr = temp;
		st->capacity = newCapacity;
	}

	st->arr[st->top] = data;
	st->top++;

}
//出栈
void StackPop(Stack* st) {
	assert(st);
	assert(st->top != 0);

	st->top--;
}
//获取栈顶元素
ElemType StackTop(Stack* st) {
	assert(st);
	assert(st->top != 0);

	return st->arr[st->top - 1];
}
//获取栈中有效元素的个数
int StackSize(Stack* st) {
	assert(st);
	return st->arr[st->top];
}
//检测栈是否为空
int StackEmpty(Stack* st) {
	assert(st);
	return st->top == 0;
}
//销毁栈
void StackDestroy(Stack* st) {
	assert(st);
	if (st->arr) {
		free(st->arr);
		st->arr = NULL;
	}
	st->capacity = 0;
	st->top = 0;
}

 聪明的你肯定知道,的特点是后进先出(LIFO),队列的特点是先进先出(FIFO),那我们怎么实现用2个栈实现一个队列呢?咱们先来画个图~

①定义2个栈,分别为Pushst(专门用来存储元素)和Popst(专门用来移除栈顶元素),初始化2个栈,2个栈都为空。

②往命名为Pushst的栈里面存储元素, 假设我们存储 1, 2, 3, 4,就可以直接把数据存储到Pushst栈中

③如果此时我们想要获取栈底元素“1”(也就是队头元素“1”),我们本应该从Popst栈里面获取栈顶元素(队头元素),但是现在Popst栈为空。因此,我们需要将Pushst栈里面的元素全部拷贝到Popst栈中。注意啦!Pushst 栈里面的数据拷贝到Popst栈中时,会改变原来的顺序!

举个例子呗~ 还是上面这幅图

第一次:  将Pushst栈的栈顶元素"4"拷贝到Popst栈中,再把"4"这个值从Pushst栈里面删除

第二次:  将Pushst栈的栈顶元素"3"拷贝到Popst栈中,再把"3"这个值从Pushst栈里面删除

第三次:  将Pushst栈的栈顶元素"2"拷贝到Popst栈中,再把"2"这个值从Pushst栈里面删除

第四次:将Pushst栈的栈顶元素"1"拷贝到Popst栈中,再把"1"这个值从Pushst栈里面删除

OK,所有数据从Pushst栈里面拷贝到Popst栈里面,我们可以发现,在Pushst栈里面,数据是以 4->3->2->1 的形式存储的,当数据拷贝到Popst栈里面时, 数据是以 1->2->3->4的形式存储的,刚好符合队列的先进先出的顺序 ,现在Popst的栈顶元素为"1",刚好是我们要查找的队头元素,最后我们将栈顶元素"1"(也就是队头元素"1")返回就可以啦!

④我们要移除队头元素(Popst栈里面的栈顶元素)

哈哈哈,这个问题就是在查询队头元素(查询栈顶元素)的基础上,将栈顶元素删除就可以啦!

方法和上述步骤相同,当Popst栈为空时,将Pushst栈里面所有的数据依次拷贝到Popst栈中,然后将Popst栈的栈顶元素返回,最后将这个Popst栈的栈顶元素移除。

⑤判断2个栈组成的队列是否为空

在上一篇中,我们知道,只有2个队列同时为空时,组成的栈才为空; 那在这一篇中,我们也可以想到,只有当2个栈同时为空时,组成的队列才为空

⑥销毁队列

实现完由2个栈组成的队列基本操作后,我们就可以销毁这个队列啦! 怎么销毁呢? 那就是先销毁2个栈,最后将申请在堆上的malloc的内存空间释放掉。

OK! 这道题我们讲完了,完整代码如下:

typedef int ElemType;
typedef struct Stack {
	ElemType* arr;
	int top;
	int capacity;
}Stack;

//初始化栈
void StackInit(Stack* st);
//入栈
void StackPush(Stack* st, ElemType data);
//出栈
void StackPop(Stack* st);
//获取栈顶元素
ElemType StackTop(Stack* st);
//获取栈中有效元素的个数
int StackSize(Stack* st);
//检测栈是否为空
int StackEmpty(Stack* st);
//销毁栈
void StackDestroy(Stack* st);

//初始化栈
void StackInit(Stack* st) {
	assert(st);
	st->arr = NULL;
	st->top = 0;
	st->capacity = 0;

}
//入栈
void StackPush(Stack* st, ElemType data) {
	assert(st);
	//扩容
	if (st->capacity == st->top) {
		int newCapacity = st->capacity == 0 ? 4 : 2 * (st->capacity);
		ElemType* temp = realloc(st->arr, newCapacity * sizeof(ElemType));
		if (temp == NULL) {
			perror("realloc fail!\n");
			exit(1);
		}
		st->arr = temp;
		st->capacity = newCapacity;
	}

	st->arr[st->top] = data;
	st->top++;

}
//出栈
void StackPop(Stack* st) {
	assert(st);
	assert(st->top != 0);

	st->top--;
}
//获取栈顶元素
ElemType StackTop(Stack* st) {
	assert(st);
	assert(st->top != 0);

	return st->arr[st->top - 1];
}
//获取栈中有效元素的个数
int StackSize(Stack* st) {
	assert(st);
	return st->arr[st->top];
}
//检测栈是否为空
int StackEmpty(Stack* st) {
	assert(st);
	return st->top == 0;
}
//销毁栈
void StackDestroy(Stack* st) {
	assert(st);
	if (st->arr) {
		free(st->arr);
		st->arr = NULL;
	}
	st->capacity = 0;
	st->top = 0;
}


typedef struct {
    //用一个栈用来填充数据,一个用来删除数据
    Stack Pushst;       
    Stack Popst;
} MyQueue;


MyQueue* myQueueCreate() {
    //用malloc申请一块空间给 MyQueue,开辟2个栈的结构体
    MyQueue* Q = (MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&Q->Pushst);          //分别将两个栈初始化
    StackInit(&Q->Popst);
    return Q;                       //将指向结构体的指针Q返回
}

void myQueuePush(MyQueue* obj, int x) {
    StackPush(&obj->Pushst,x);      //往Pushst栈里面填充数据
}   

int myQueuePop(MyQueue* obj) {      
        if(StackEmpty(&obj->Popst)){   
        //如果Popst栈为空 
        //那么就将Pushst栈里面所有的元素拷贝到Popst栈中
        while(!StackEmpty(&obj->Pushst)){   
            int top = StackTop(&obj->Pushst);//获取Pushst栈里面的栈顶元素
            StackPush(&obj->Popst,top);      //拷贝到Popst栈中
            StackPop(&obj->Pushst);          
        }
    }
   int ret = StackTop(&obj->Popst);          //获取Popst栈中的栈顶元素
   StackPop(&obj->Popst);                    //将栈顶元素移除
    return ret;                              //将栈顶元素返回
}

int myQueuePeek(MyQueue* obj) {
        if(StackEmpty(&obj->Popst)){  
            //如果Popst栈为空 
        //那么就将Pushst栈里面所有的元素拷贝到Popst栈中      
        while(!StackEmpty(&obj->Pushst)){   
            int top = StackTop(&obj->Pushst);//获取Pushst栈里面的栈顶元素
            StackPush(&obj->Popst,top);      //拷贝到Popst栈中
            StackPop(&obj->Pushst);          //将这个元素从Pushst栈中移除
        }
    }
    return StackTop(&obj->Popst);            //将栈顶元素返回    
}

bool myQueueEmpty(MyQueue* obj) {
    //只有当2个栈同时为空,队列才为空
    return StackEmpty(&obj->Pushst) && StackEmpty(&obj->Popst);
}

void myQueueFree(MyQueue* obj) {
    StackDestroy(&obj->Pushst); //销毁Pushst栈
    StackDestroy(&obj->Popst);  //销毁Popst栈
    free(obj);                  //将开辟的内存空间返回
}

不过,这里有一个小小的注意点,不知道小伙伴们有木有发现,这两段代码实质上是重复的~

哦! 我知道啦! 我们可以调用myQueuePeek函数来帮助我们实现myQueuePop函数! myQueuePop函数只是在myQueuePeek函数的基础上删除了栈顶元素而已~

好滴! 果然被眼光灵敏的你捕捉到了,接下来我们优化代码~

int myQueuePop(MyQueue* obj) {      
    int front = myQueuePeek(obj);       //获取Popst栈里面的栈顶元素(队头元素)
    StackPop(&obj->Popst);              //将Popst栈的栈顶元素删除
    return front;                       //返回栈顶元素
}

片尾

今天我们学习了一道OJ题---用栈实现队列,里面涉及到了有关栈和队列的基础知识,希望看完这篇文章能对友友们有所帮助 !   !   !

点赞收藏加关注 !   !   !

谢谢大家 !   !   !

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值