代码随想录算法训练营第十天 | 232.用栈实现队列,225. 用队列实现栈[栈与队列篇]

文章详细解析了如何使用两个栈实现先入先出队列(LeetCode232)和如何用两个队列实现后入先出栈(LeetCode225),涉及栈和队列的操作,以及边界条件处理和代码实现策略。
摘要由CSDN通过智能技术生成

LeetCode 232.用栈实现队列

题目链接:232.用栈实现队列
文章讲解:代码随想录#232.用栈实现队列
视频讲解:栈的基本操作! | LeetCode:232.用栈实现队列

题目描述

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:

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

示例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 <= x <= 9
  • 最多调用 100 次 push、pop、peek 和 empty
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

思路

这道题主要考虑对栈、队列特性的理解,以及使用。
栈的特性:先入后出
队列的特性:先入先出

那如何通过栈来实现队列呢?
与汉诺塔类似,一个栈肯定实现不了,需要再借助一个栈来倒转腾挪栈中的元素。这样,一个栈作为输入栈,另一个栈作为输出栈。
如图
在这里插入图片描述
有四个元素:1、2、3、4,
对于队列来说来,这四个元素进入队列顺序为1、2、3、4,出队列的顺序为1、2、3、4
对于输入栈来说,这四个元素进入栈的顺序为1、2、3、4,出栈的顺序为4、3、2、1,显示一个栈是实现不了的。
再借助一个输出栈,将输入栈的出栈元素再进入到输出栈中,
对于输出栈来 说,这四个元素进入栈的顺序为4、3、2、1,出栈的顺序为1、2、3、4。
这样就可以实现题目要求。

题目中已经要求了最多可以操作100次pop、push等操作,那我们可以声明两个数组stackIn[100]、stackOut[100],并且为每个数组再定位个变量,用来指向当前数组移动的位置。
在进行push操作时,直接给stackIn数组放数据,并修改Index。在进行pop操作时,需要进行判断,如果stackOut为空,则需要从stackIn中将数据全部挪到stackOut中,然后再输出,如果stackOut不为空,那直接输出即可。

参考代码

typedef struct {
    int stkInIdx, stkOutIdx;
    int stackIn[100], stackOut[100];
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue *q = (MyQueue*)malloc(sizeof(MyQueue));
    if (q == NULL) {
        return NULL;
    }
    q->stkInIdx = 0;
    q->stkOutIdx = 0;
    memset(&q->stackIn[0], 0, 100 * sizeof(int));
    memset(&q->stackOut[0], 0, 100 * sizeof(int));

    return q;
}

void myQueuePush(MyQueue* obj, int x) {
    obj->stackIn[obj->stkInIdx++] = x;
}

int myQueuePop(MyQueue* obj) {
    int tmp = 0;
    if (obj->stkOutIdx == 0) {
        while (obj->stkInIdx > 0) {
            obj->stkInIdx--;
            obj->stackOut[obj->stkOutIdx++] = obj->stackIn[obj->stkInIdx];
            obj->stackIn[obj->stkInIdx] = 0;
        }
    }
    obj->stkOutIdx--;
    tmp = obj->stackOut[obj->stkOutIdx];
    obj->stackOut[obj->stkOutIdx] = 0;
    return tmp;
}

int myQueuePeek(MyQueue* obj) {
    if (obj->stkOutIdx > 0) {
        return obj->stackOut[obj->stkOutIdx - 1];
    } else {
        return obj->stackIn[0];
    }
}

bool myQueueEmpty(MyQueue* obj) {
    if (obj->stkInIdx == 0 && obj->stkOutIdx == 0) {
        return true;
    }
    return false;
}

void myQueueFree(MyQueue* obj) {
    free(obj);
    obj = NULL;
}

总结

  1. 出栈时移动指针时一定要小心,比如元素有4个,stkInIdx=4,但是数组下标是0-3,这种边界处理时一定要仔细些。

LeetCode 225. 用队列实现栈

题目链接:225. 用队列实现栈
文章讲解:代码随想录#225. 用队列实现栈
视频讲解:队列的基本操作! | LeetCode:225. 用队列实现栈

题目描述

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

示例1

输入
[“MyStack”, “push”, “push”, “top”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出
[null, null, null, 2, 2, false]

解释
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

提示

  • 1 <= x <= 9
  • 最多调用100 次 push、pop、top 和 empty
  • 每次调用 pop 和 top 都保证栈不为空

思路

和上一道题类似,刚好相反,也是实现队列的基本操作,由于栈与队列的结构特性不一样,所以上一道的思路肯定行不通。
那如何用两个队列实现栈??
一个队列用来输出与栈相同的顺序的数,另一个队列用来保存多余的数,动态模拟过程可以看一下随想录的动图,最好自己在本子上画一下,好好地模拟下,就知道代码该怎么写了。

大概思路就是,队列一中有n个数,先将1到n-1个数挪到列队二中,然后输出队列一中的第n个数。
接着,将队列二中的1到n-2个数挪到队列一中,然后输出队列二中的第n-1个数。

以此类推,直到将所有的数据都输出,刚好模拟到栈的先进后出的顺序。

当然这道也可以用一个队列来实现栈,其实就是将上面的思路简化一下,在队列弹出元素的时候,只需要将1到n-1个数重新添加到到队列的队尾。
比较偷巧的办法就是用一个链表,每次push时头插一个节点,pop时就从头节点取出这个值。

参考代码

typedef struct stack{
    int val;
    struct stack *next;
} MyStack;


MyStack* myStackCreate() {
    MyStack *stack = (MyStack*)malloc(sizeof(MyStack));
    stack->next = NULL;
    return stack;
}

void myStackPush(MyStack* obj, int x) {
    MyStack *node = (MyStack*)malloc(sizeof(MyStack));
    node->next = obj->next;
    node->val = x;
    obj->next = node;
}

int myStackPop(MyStack* obj) {
    MyStack *tmp = obj->next;
    int val = tmp->val;
    obj->next = tmp->next;
    free(tmp);
    return val;
}

int myStackTop(MyStack* obj) {
    return obj->next->val;
}

bool myStackEmpty(MyStack* obj) {
    if (obj->next == NULL) {
        return true;
    }
    return false;
}

void myStackFree(MyStack* obj) {
    while (obj->next != NULL) {
        MyStack *tmp = obj->next;
        obj->next = tmp->next;
        free(tmp);
    }
    free(obj);
}

总结

  1. 这道题用链表实现总有点儿胜之不武的感觉,严格意义来说,并没有使用队列的特性。用两个队列来实现算是比较好,以后有机会二刷的时候,可以尝试下。
  2. 链表的基本操作还是蛮重要的,边界判断永远是最关键的。双向链表的增删改查以后有机会也得多练习,linux内核中大量使用链表来挂接各个数据结构,与之产生了几个比较重要的宏,比如offsetof、container_of等,可以看一下这篇文章,了解一下。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值