题目描述
用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。
数据范围: n≤1000n≤1000
要求:存储n个元素的空间复杂度为 O(n)O(n) ,插入与删除的时间复杂度都是 O(1)O(1)
题目分析
首先,题目明确指出我们只能使用两个栈作为数据结构,并且队列中的元素必须是整数类型。这意味着我们不能依赖队列或其他数据结构的特性,而必须完全依靠栈的操作来实现队列的功能。
接下来,题目要求我们支持 n
次的 push
操作和 n
次的 pop
操作。push
操作需要在队列尾部插入元素,而 pop
操作需要从队列头部移除元素。重要的是,pop
操作必须在队列非空的情况下执行,以避免非法操作。
此外,题目对性能有严格的要求。存储 n
个元素的空间复杂度必须是 O(n)
,而 push
和 pop
操作的时间复杂度都必须是 O(1)
。这要求我们的实现必须高效,以保证操作的快速性。
解题思路
首先,我们得明白栈和队列的区别。栈是后进先出,队列是先进先出。我们要用两个栈来模拟队列的行为。
-
入队操作:将元素推入
stackIn
,这是一个直接的栈操作,模拟队列的入队行为。 -
出队操作:这是实现中的关键部分。如果
stackOut
不为空,我们直接从stackOut
弹出元素,这模拟了队列的出队行为。如果stackOut
为空,我们需要将stackIn
中的所有元素转移到stackOut
,这一步骤虽然时间复杂度为O(n)
,但由于其在整个操作序列中只发生一次,因此不会影响pop
操作的整体平均时间复杂度。 -
性能保证:通过上述策略,我们可以确保
push
操作的时间复杂度为O(1)
,而pop
操作在大多数情况下也是O(1)
,只有在stackOut
为空时需要进行一次额外的元素转移。 -
单例对象的使用:我们将实现封装在一个单例对象
Solution
中,这样可以确保全局只有一个队列实例,并且可以通过这个单例对象来执行所有队列操作。
具体实现
object Solution {
private val stackIn = mutableListOf<Int>() // 用于入队的栈
private val stackOut = mutableListOf<Int>() // 用于出队的栈
/**
* 入队操作
* @param node int整型 入队的元素
* @return 无
*/
fun push(node: Int) {
stackIn.add(node)
}
/**
* 出队操作
* @param 无
* @return int整型 队列头部的元素
*/
fun pop(): Int {
if (stackOut.isEmpty()) {
// 当stackOut为空时,将stackIn中的元素依次压入stackOut,实现反转
while (stackIn.isNotEmpty()) {
stackOut.add(0, stackIn.removeAt(stackIn.lastIndex))
}
}
// 弹出并返回stackOut的第一个元素,即队列的队首元素
return stackOut.removeAt(0)
}
}
fun main() {
// 执行入队操作
repeat(5) {
Solution.push(it + 1)
}
// 执行出队操作,并打印结果
repeat(5) {
println("Popped item: ${Solution.pop()}")
}
}