力扣(leetcode)--用栈实现队列--c语言--数据结构oj题

题目的所有代码在文章末尾处

用队列实现栈的进阶版--详情见上篇博客--先学习上篇才能更好理解哦

a.用队列实现栈讲解链接

用队列实现栈icon-default.png?t=N7T8https://blog.csdn.net/2303_77756141/article/details/140542050?app_version=6.3.9&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22140542050%22%2C%22source%22%3A%222303_77756141%22%7D&utm_source=app

 

实际上,我觉得 栈实现队列 比用 队列实现栈 的题好做一点,可能是因为先做了 队列实现栈 思路更清晰了,接下来让我们一起来做用栈实现队列这道oj题吧。

b.题目链接

用栈实现队列icon-default.png?t=N7T8https://leetcode.cn/problems/implement-queue-using-stacks/description/

 c.题目

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 你 只能 使用标准的栈操作 —— 也就是只有 push to toppeek/pop from topsize, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

题目所给的代码:

typedef struct {

   

} MyQueue;


 

MyQueue* myQueueCreate() {

   

}

void myQueuePush(MyQueue* obj, int x) {

   

}

int myQueuePop(MyQueue* obj) {

   

}

int myQueuePeek(MyQueue* obj) {

   

}

bool myQueueEmpty(MyQueue* obj) {

   

}

一、题意解析

a. 需要明白几点:

1.队列是什么?

2.栈是什么?

3.它们之间的区别是什么?

具体可见博客 

栈和队列的概念和实现icon-default.png?t=N7T8https://blog.csdn.net/2303_77756141/article/details/140506549?app_version=6.3.9&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22140506549%22%2C%22source%22%3A%222303_77756141%22%7D&utm_source=app

通过清楚他们的概念与各种区别,现在我们可以开始做题了

b.思路

队列是先进先出,而栈是先进后出,要想用队列实现栈肯定需要将队的顺序进行一个调转。

队列删除是删除的队列最先中最早进去的元素,队列插入是从队尾入队;

栈删除是删除的是栈中最后进去的那个元素,即栈的栈顶元素,插入是从栈顶插入。

示例 1:

输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

*核心思想: 

一个栈用于存数据,一个栈用于出数据,用于出数据的栈,直到栈空,再让新的元素进栈。

1.入队

如图,此时栈2所存元素的顺序,便是队列存好元素的顺序。

2.出队

如图,此时栈2的元素所存的元素仍然是队列所存元素的顺序。因此在此时出队1,2,实际上就是在栈2中将 1,2 依次pop掉

3.出栈后元素需要再倒回去吗?

请认真思考这个问题。可以自行画图看看。

1.把栈2中元素倒回栈1

让我们先画一画把栈2中元素倒回栈1后怎样怎样又获得新的队列

如图,需要经过a,b,c三次变换,才能再次得到新的队列从而能取到队头元素。

2.直接在栈1中添加元素

让我们先画一画直接在栈1中添加元素会怎么样

如图,此时若想pop队列中的元素,直接pop栈2的栈顶元素便可以了。而在 1 中需要经过a,b,c三个过程,何不将栈2中的元素pop完之后再装入栈1中的元素 6,7,8 呢,再去看 1 中是不是也是一样,将 3,4,5 pop掉之后,栈2中只剩6,7,8这样至顶而下的排列,因此我们得到这样的思路:将栈2中的元素全pop完之后,再将栈1中的元素push进栈2,即,用完再添。此时我们再去看核心思想是不是就清晰多了。

二、每个函数实现的具体讲解

1. 定义两个用于分别实现进队出队的结构体
也就是两个栈

typedef struct {

    ST pushst;
    ST popst;   

} MyQueue;

1. 注意这里定义的是结构体,而不是结构体的指针,结构体的指针是地址


2. 如果用的是结构体指针,在(MyQueue*)malloc....的时候,没有初始化这两个指针就是野指针,初始化过后就是空指针。


3. 而在初始化的时候,要求传入的参数是一个实际的指针


a. 空指针断言直接报错,
b. 野指针在pst->a,capacity,top时报错,因为野指针指向一个不属于它的空间


4.如果非要使用结构体指针, 还需在myQueueCreate()中进行两次 malloc ST 的结构变量, 将这两个结构体指针的空间给开辟出来,再用于初始化


5. 指针是一个地址

2. 用栈实现初始化队列

MyQueue* myQueueCreate() {
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    STInit(&obj->pushst);

}

3. 用栈实现从队尾入队

void myQueuePush(MyQueue* obj, int x) {
    STPush(&obj->pushst, x);
}

也就是让元素进入存元素的栈

5. 用栈实现 返回队列开头的元素,与pop是有区别的

int myQueuePeek(MyQueue* obj) {
    if (STEmpty(&obj->popst))
    {
        while (!STEmpty(&obj->pushst))
//当pushst中的值全都进到popst循环停止
                                                           //即pushst中为空

        {
            STPush(&obj->popst, STTop(&obj->pushst));
            STPop(&obj->pushst); 
        }
    }
    return STTop(&obj->popst);
}

1. 看数据但是不删除,

2. 就是将队列的头取出来,即最早进入的那个数据

3. 也就是取栈的尾部,因此又要将数据放入popst

4. 用栈实现队头元素出队

int myQueuePop(MyQueue* obj) {
    int front = myQueuePeek(obj);//存该元素
    STPop(&obj->popst);
    return front;
}

6.用栈实现队列的判空

bool myQueueEmpty(MyQueue* obj) {

    return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}

7.用栈实现队列的销毁

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->pushst);
    STDestroy(&obj->popst);
    free(obj);
    obj = NULL;
}

注意:不要忘记先销毁开辟出来的两个栈,因为开辟出来的两个栈一直有空间

8.测试用例

这里我写了一个测试用例,方便大家用来调试检测代码中的错误。

 

int main()
{
    MyQueue* obj = myQueueCreate();
    myQueuePush(obj,1);
    myQueuePush(obj,2);
    printf("%d ", myQueuePeek(obj));

    printf("%d ", myQueuePop(obj));
    myQueueEmpty(obj);
    return 0;

 }

三、完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;	
	int capacity;
}ST;

void STInit(ST* pst);
void STDestroy(ST* pst);


void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);

bool STEmpty(ST* pst);
int STSize(ST* pst);

void STInit(ST* pst)
{
	assert(pst);

	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0;
}
void STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

void STPush(ST* pst, STDataType x)
{
	assert(pst);

	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}

	pst->a[pst->top] = x;
	pst->top++;
}
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);

	return pst->a[pst->top - 1];
}


bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}
int STSize(ST* pst)
{
	assert(pst);

	return pst->top;

}
typedef struct {
	ST pushst;
	ST popst;

}MyQueue;

MyQueue* myQueueCreate() {
	MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
	STInit(&obj->pushst);
	STInit(&obj->popst);

	return obj;
}

void myQueuePush(MyQueue* obj, int x) {
	STPush(&obj->pushst, x);
}

int myQueuePop(MyQueue* obj) {
	int front = myQueuePeek(obj);
	STPop(&obj->popst);
	return front;
}

int myQueuePeek(MyQueue* obj) {
	
	if (STEmpty(&obj->popst))
	{
		while (!STEmpty(&obj->pushst))
		{
			STPush(&obj->popst, STTop(&obj->pushst));
			STPop(&obj->pushst);
		}
	}
	return STTop(&obj->popst);
}

bool myQueueEmpty(MyQueue* obj) {

	return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}

void myQueueFree(MyQueue* obj) {
	STDestroy(&obj->pushst);
	STDestroy(&obj->popst);
	free(obj);
	obj = NULL;
}

结语:

       在探索这道题的解答过程中,我深刻体会到知识的海洋浩瀚无垠,每一次解题都是一次自我挑战与成长的宝贵机会。虽然我已经尽力将解题思路和步骤清晰地呈现出来,但深知学无止境,仍有诸多改进和优化的空间。如果您在阅读过程中发现了更好的解法或是有任何疑问和建议,都非常欢迎您在评论区留言,让我们共同探讨,共同进步。

       最后,如果您觉得这篇博客对您有所帮助,或是受到了些许启发,不妨动动手指,给予一个小小的鼓励——点个赞、收藏起来以便日后回顾,甚至分享给更多需要帮助的朋友。您的每一次互动,都是对我最大的支持与鼓励,也是我继续创作更多优质内容的动力源泉。

       再次感谢您的阅读,期待在未来的解题之旅中,与您再次相遇,共同解锁更多知识的奥秘!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值