链接
https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/
难度: #简单
题目
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
["CQueue","appendTail","deleteHead","deleteHead"]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:
输入: ["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
提示:
- 1 <= values <= 10000
- 最多会对 appendTail、deleteHead 进行 10000 次调用
代码框架
class CQueue {
public CQueue() {
}
public void appendTail(int value) {
}
public int deleteHead() {
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
题目解析
该题本质上是实现一个队列,
队列的特点是先进先出,后进后出,
appendTail往队列尾部添加元素,
deleteHead从队列头部删除元素。
使用两个栈tail和head,
栈tail的栈顶是最后加入的整数,
栈head的栈顶是最先加入的整数。
添加整数时往栈tail里面push。
删除整数时从栈head里面pop,
如果pop出来的是null, 则表示栈head为空,
继续检查栈tail是否为空,
如果也为空,则表示队列已经空了, 此时返回-1;
如果不为空,则从栈tail里面pop一个整数,
每pop一个整数就往head里面push,
直到栈tail里面pop出来的是null,
这时再去从栈head里面pop,
即可返回栈顶最先加入的数字。
该题目给出的输入有2行,
第1行是操作的函数,
第2行是对应的入参,
[]表示函数没有入参,
输出表示函数的返回,
[]表示函数没有返回值,是void。
该题目给出的测试用例太过简单,
不方便思考,
下面使用5个整数演示思路:
解答思路1:
使用Java自带的Stack栈实现,
但是使用Stack的方式来做这道题,会造成速度较慢;
原因的话是Stack继承了Vector接口,
而Vector底层是一个Object[]数组,
空间扩容和移位的会增加 执行用时。
解答思路2:
使用LinkedList来做Stack的容器,
因为LinkedList实现了Deque接口,
所以Stack能做的事LinkedList都能做,
其本身结构是个双向链表,扩容消耗少。
但是不要像别人提交的执行时间超过100%的代码那样,
直接使用一个LinkedList当做队列,
那样确实是快,但是不符题意。
测试用例
package edu.yuwen.sowrd.num09.solution;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import edu.yuwen.sowrd.num09.sol2.CQueue;
public class CQueueTest {
/**
* 输入:
* ["CQueue","appendTail","deleteHead","deleteHead"]
* [[],[3],[],[]]
* 输出:[null,null,3,-1]
*/
@Test
public void testCase1() {
CQueue queue = new CQueue();
queue.appendTail(3);
int res = queue.deleteHead();
Assertions.assertEquals(3, res);
res = queue.deleteHead();
Assertions.assertEquals(-1, res);
}
/**
* 输入:
* ["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"]
* [[],[],[5],[2],[],[]]
* 输出:[null,-1,null,null,5,2]
*/
@Test
public void testCase2() {
CQueue queue = new CQueue();
int res = queue.deleteHead();
Assertions.assertEquals(-1, res);
queue.appendTail(5);
queue.appendTail(2);
res = queue.deleteHead();
Assertions.assertEquals(5, res);
res = queue.deleteHead();
Assertions.assertEquals(2, res);
}
}
解答1
package edu.yuwen.sowrd.num09.sol1;
import java.util.Stack;
/**
* 解答1:使用Java的Stack栈
*
*/
public class CQueue {
Stack tail;
Stack head;
public CQueue() {
tail = new Stack<>();
head = new Stack<>();
}
public void appendTail(int value) {
tail.push(value);
}
public int deleteHead() {
// 第一次pop,
Integer res = popOrNull(head);
// 如果不为null,则返回结果
if (res != null) {
return res;
}
// 如果为null,则需要转移tail的数字到head
transferTail2Head();
// 第二次pop
res = popOrNull(head);
// 如果为null,则队列空,返回-1
if (res == null) {
return -1;
}
return res;
}
private void transferTail2Head() {
Integer num = popOrNull(tail);
while (num != null) {
head.push(num);
num = popOrNull(tail);
}
}
/**
* pop前先进行检查, 当栈为空时,返回null,而不是抛出异常
*/
private Integer popOrNull(Stack stack) {
if (stack.isEmpty()) {
return null;
}
return stack.pop();
}
}
解答2 推荐
package edu.yuwen.sowrd.num09.sol2;
import java.util.Deque;
import java.util.LinkedList;
/**
*
* 解法2:使用Deque来替代Stack,仅限使用Deque的两个方法模拟栈的后进先出
*
*/
public class CQueue {
// 使用Deque的addLast模拟栈的push
// 使用Deque的pollLast模拟栈的pop,不使用removeLast的原因是队列为空时,会抛出异常
Deque tail;
Deque head;
public CQueue() {
// LinkedList实现了队列
tail = new LinkedList<>();
head = new LinkedList<>();
}
public void appendTail(int value) {
tail.addLast(value);
}
public int deleteHead() {
if (head.isEmpty()) {
if (tail.isEmpty()) {
// 确定队列空,返回-1
return -1;
}
Integer num = tail.pollLast();
while (num != null) {
head.addLast(num);
num = tail.pollLast();
}
}
// 因为判断了tail不为空,队列至少有一个元素
return head.pollLast();
}
}