更详细的讲解和代码调试演示过程,请参看视频
如何进入google,算法面试技能全面提升指南
如果你对机器学习感兴趣,请参看一下链接:
机器学习:神经网络导论
队列的插入和删除遵循先入先出的原则,而堆栈元素的插入和删除遵循后进先出的原则。在很多应用场景下,我们需要使用堆栈来模拟队列,或者是使用队列来模拟堆栈。在数学上,已经能够严格证明,我们是不能使用含有n个元素的堆栈来模拟含有n个元素的队列的,这个证明非常复杂,详细证明可以参看论文: The Power of the queue.
用一个堆栈来模拟一个队列是不可能的,但是用两个堆栈来模拟一个队列确是可能的,问题是:如何使用两个堆栈来模拟一个队列,实现两种操作,enqueue 和 dequeue, enqueue是在队列末尾加入一个元素,dequeue是把队列的头部元素取出来。要求不能分配超过O(1)的内存,同时要求当进行m次enqueue或dequeue操作时,算法复杂度必须是O(m).
我们的做法是这样的,两个堆栈,假设分别为StackA, 和 StackB, 当使用enqueue将元素加入堆栈时,把该元素直接压入StackA, 如果需要使用dequeue将队列头元素出栈时,可以直接把StackB中的元素出栈。如果出栈时StackB中没有元素,那么把StackA中的元素逐个弹出然后压入StackB.
例如我们通过enqueue操作构造的一个队列如下:
1<- 2 <- 3 <- 4 <- 5
由于是enque操作,所以上面元素依次压入StackA.
StackA:
5
4
3
2
1
stackB: null
如果此时要dequeue获取队列的头元素1,那么我们把StackA中的元素逐个弹出,然后再压入B, 于是先弹出5,然后把5压入B, 依次进行后情况为:
StackA: null
StackB:
1
2
3
4
5
然后把B中的头元素弹出,于是数值1就可以获取了。
该算法不难,关键是如何证明该算法满足时间复杂度O(n).
每次执行enqueue操作是,我们只需要把元素通过push加入StackA,所以时间复杂度是O(1), 问题在于,如果执行dequue操作,我们有可能需要把元素从StackA 全部弹出,然后再压入StackB, 这个过程的时间复杂度是O(n)。事实上,任何一个元素,它能被操作的次数最多不过3次,一次是压入堆栈,一次是从StackA转移到StackB, 一次是从StackB中弹出,所以m次enqueue和dequue操作,其时间复杂度是3*m, 因此我们的算法时间复杂度是线性的。
用于我们算法中没有分配多余内存,因此空间复杂度是O(1).算法的实现代码如下:
import java.util.Stack;
public class StackQueue {
private Stack<Integer> stackA = new Stack<Integer>();
private Stack<Integer> stackB = new Stack<Integer>();
public void enquene(int v) {
stackA.push(v);
}
public int dequeue() {
if (stackB.isEmpty()) {
while (!stackA.isEmpty()) {
stackB.push(stackA.pop());
}
}
return stackB.pop();
}
}
主入口处的代码为:
import java.util.ArrayList;
import java.util.Random;
import java.util.Stack;
public class StackAndQuque {
public static void main(String[] args) {
StackQueue sq = new StackQueue();
System.out.println("enqueue: ");
for (int i = 1; i <=5; i++) {
sq.enquene(i);
System.out.print(i + " ");
}
System.out.println("\ndequeue: ");
for(int i = 0; i < 5; i++) {
System.out.print(sq.dequeue() + " ");
}
}
}
代码执行后的结果为:
enqueue:
1 2 3 4 5
dequeue:
1 2 3 4 5
我们可以看到,元素的入队和出队遵守先入先出的次序,因此,我们代码的实现应该是正确的。
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
网友评论