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;
}
总结
- 出栈时移动指针时一定要小心,比如元素有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);
}
总结
- 这道题用链表实现总有点儿胜之不武的感觉,严格意义来说,并没有使用队列的特性。用两个队列来实现算是比较好,以后有机会二刷的时候,可以尝试下。
- 链表的基本操作还是蛮重要的,边界判断永远是最关键的。双向链表的增删改查以后有机会也得多练习,linux内核中大量使用链表来挂接各个数据结构,与之产生了几个比较重要的宏,比如offsetof、container_of等,可以看一下这篇文章,了解一下。