两个栈实现一个队列

建了博客,可是一个字也没写过。今天写这个练手的简单小题目,看看会不会被砖头拍死。两个栈实现一个队列,不考虑效率的确不是什么难事。于是,我懒得做任何优化,只是尽量把代码写干净一点,用Java。接口和java.util.Queue类似(可能不是完全一样):offer方法表示入队,poll方法表示出队,peek方法是提取队头元素,empty方法检测队列是否为空。

思路很简单——我看了题目很快就能想出来思路的,想必是不难。想象两个栈A和B,一开始里面都没有元素。

  • 入队(offer)时,只是在A中压入相应的元素。
  • 出队(poll)时,由于需要的是A底部的元素,所以把A种的元素逐个弹出放入B中,就可以把顺序颠倒过来,使得A的底部元素成为B的顶部元素(ensureDequeue方法和exchangeStack方法)。
  • 访问队列顶部元素(peek)和出队很接近,不用单独说了。
  • 回到入队,如果B不是空的,那么需要和出队相反的操作(ensureEnqueue方法)。布尔变量mEnqueue用来区分当前是处于入队还是出队的状态,这样就可以决定要不要调用exchangeStack方法来”倾倒“某个栈。

import java.util.Stack;

class MyQueue<T> {
    private Stack<T> mStackEnqueue;
    private Stack<T> mStackDequeue;
    private boolean mEnqueue;
    
    public MyQueue() {
        mStackEnqueue = new Stack<T>();
        mStackDequeue = new Stack<T>();
        mEnqueue = true;
    }
    
    public void offer(T value) {
        ensureEnqueue();
        mStackEnqueue.push(value);
    }
    
    public T poll() {
        ensureDequeue();
        if (mStackDequeue.empty()) return null;
        return mStackDequeue.pop();
    }
    
    public T peek() {
        ensureDequeue();
        if (mStackDequeue.empty()) return null;
        return mStackDequeue.peek();
    }
    
    public boolean empty() {
        if (mEnqueue) {
            return mStackEnqueue.empty();
        }
        return mStackDequeue.empty();
    }
    
    private void ensureEnqueue() {
        if (!mEnqueue) {
            exchangeStack(mStackDequeue, mStackEnqueue);
            mEnqueue = true;
        }
    }
    
    private void ensureDequeue() {
        if (mEnqueue) {
            exchangeStack(mStackEnqueue, mStackDequeue);
            mEnqueue = false;
        }
    }
    
    private void exchangeStack(Stack<T> src, Stack<T> dst) {
        assert dst.empty();
        while (!src.empty()) {
            T tmp = src.pop();
            dst.push(tmp);
        }
    }
}

public class Stack2Queue {
    public static void main(String[] args) {
        System.out.println("Start testing MyQueue");
        MyQueue<Integer> myQueue = new MyQueue<Integer>();
        assert myQueue.empty();
        int length = 10;
        // enqueue
        for (int k = 0; k < length; ++k) {
            myQueue.offer(Integer.valueOf(k));
            assert myQueue.peek().intValue() == 0;
            assert !myQueue.empty();
        }
        
        // dequeue
        for (int k = 0; k < length; ++k) {
            assert myQueue.peek().intValue() == k;
            assert !myQueue.empty();
            Integer kWrapped = myQueue.poll();
            assert kWrapped.intValue() == k;
        }
        
        // dequeue when queue is empty
        assert myQueue.empty();
        Integer nullInteger = myQueue.poll();
        assert nullInteger == null;
        assert myQueue.empty();
        
        // interleaved enqueue and dequeue
        for (int k = 0; k < length; ++k) {
            myQueue.offer(Integer.valueOf(k));
        }
        for (int k = 0; k < length; ++k) {
            myQueue.offer(Integer.valueOf(k + length));
            Integer kWrapped = myQueue.poll();
            assert kWrapped.intValue() == k;
        }
        for (int k = 0; k < length; ++k) {
            Integer kWrapped = myQueue.poll();
            assert kWrapped.intValue() == k + length;
        }
        System.out.println("Finish testing MyQueue");
    }
}

我用了一堆assert来测试,只是因为我好久不用assert了。Java里直接写上assert是不够的,需要把你虚拟机的断言支持打开,网上一搜一堆,不再赘述。写完这段小程序,上网查了一下,发现可以优化的地方还是有的。比如:

  • mEnqueue这个状态变量可以拿掉,直接用两个栈是否为空来作为判断当前状态的条件。
  • exchangeStack方法中,不用把某一个栈完全倒空。比如,A中有若干元素,此时需要出队,那么出栈时A中的最后一个元素不用放到B中,直接拿出来就是要出队的元素。这样操作之后,两个栈的状态不会和上面给出的程序有任何不同,但是省了一次入栈操作。
如此等等。但是,我始终觉得这个问题充其量只能练练手,没有实用价值。当你入队N个元素,然后交替进行入队和出队操作,麻烦就来了。因为每一次操作,都需要对Θ(n)个元素做一次出栈和入栈操作,费力不讨好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值