棧和隊列相互實現

背景

一道很經典的數據結構的題目實現。

棧:一般是後進先出的順序,可以看下java中的Stack這個類。

隊列:一般是先進先出的順序,但是java中的Queue接口中也寫了註釋,沒有要求是必須嚴格的先進先出,比如java中也有優先級隊列、雙端隊列Deque。

代碼實現

在代碼的註釋中有描述對應的思路,這裏不去贅述

  • 兩個棧實現隊列
/**
 * Created by zlj on 2020/3/18.
 * 兩個棧 實現 隊列
 *
 * 思路:
 * 1.入棧:直接壓棧進入stackOne
 * 2.出棧:
 *      (1)判斷stackOne是否爲空,如果不爲空,則將stackOne中的數據導入stackTwo。取出stackTwo彈棧的元素
 *      (2)判斷stackTwo是否爲空,如果不爲空,重複(1)操作
 *
 */
public class StackToQueue<T> {

    Stack<T> stackOne = new Stack<>();
    Stack<T> stackTwo = new Stack<>();

    /**
     * 入隊
     * @param data
     */
    private void push(T data) {
        stackOne.push(data);
    }

    /**
     * 出隊列
     * @return
     */
    private T pop() {
        if (stackOne.empty() && stackTwo.empty())
            return null; // 容錯 棧的pop方法如果沒有元素了會報錯
        // 其實加不加這個判斷相當於Queue接口中poll(做了容錯) 和 remove(沒做容錯) 兩個方法的區別

        while (!stackOne.isEmpty()) {
            // 彈棧到stackTwo
            stackTwo.push(stackOne.pop());
        }

        // 這時彈棧 stackTwo中的元素
        T stackTwoFirstEmt = stackTwo.pop();

        // 如果出隊列的時候元素都在stackTwo
        while(!stackTwo.isEmpty()) {
            // 數據倒回stackOne 以便下一次pop的時候 再利用棧的特性 實現隊列出隊的順序
            stackOne.push(stackTwo.pop());
        }

        return stackTwoFirstEmt;
    }

    public static void main(String[] args) {
        StackToQueue<Integer> stackToQueue = new StackToQueue<>();

        // 隊列入隊
        Stream.iterate(1, i -> i+1).limit(10).forEach(stackToQueue::push);

        // 隊列出隊
        for(int i = 0; i< 10000; i++) {
            Integer pop = stackToQueue.pop();
            if (pop == null) break;
            System.out.println("隊列出隊:" + pop);
            try {
                Thread.sleep(200);
            } catch (Exception e) {

            }

        }
    }

}

  • 兩個隊列實現棧
/**
 * Created by zlj on 2020/3/19.
 * 兩個隊列實現棧
 * 思路: 維護兩個隊列 Q1 和 Q2
 * (1)入棧:即爲隊列Q1中加入元素
 * (2)出棧: 關鍵就是保持隊列q1和q2一直有一個爲空
 *
 *          - 首先元素入q1,隊列中爲 [tail]xn->xn-1...->x1 [head]  這時將q1中的n-1個元素入q2 q2中元素: [tail] xn-1->xn-2...->x1 [head]
 *          - xn從q1中出隊
 *          - 這時q1爲空,q2有n-1個元素,重複第一步,只不過現在是將q2中的n-2個元素出隊放入q1中,再將q2中的xn-1出隊即可。
 *          - 重複操作直到 q1和q2都爲空爲止
 */
public class TwoQueueToStack<T> {

    // 這裏用的Deque 雖然只是需要的是隊列 先進先出(FIFO)的特性 但其實Deque既提供了stack的操作、又提供了queue的操作,也提供了對first和end的操作(LinkedList裏面叫做head和tail)
    Queue<T> queue1 = new ArrayDeque<>();
    Queue<T> queue2 = new ArrayDeque<>();

    /**
     * 棧的入棧操作
     * @param element
     */
    void push(T element) {
        queue1.offer(element); // 比add更友好
    }


    /**
     * 棧的出棧操作
     *
     * @return
     */
    T pop() {

        if (!queue1.isEmpty()) {
            while (queue1.size() > 1) {
                // q1出隊 入隊q2
                queue2.offer(queue1.poll());
            }
            // q1的size是1了
            return queue1.poll();
        }

        if (!queue2.isEmpty()) {
            while (queue2.size() > 1) {
                queue1.offer(queue2.poll());
            }
            // q2的size是1了
            return queue2.poll();
        }

        return null;
    }

    int size() {
        if (!queue1.isEmpty()) {
            return queue1.size();
        } else if (!queue2.isEmpty()) {
            return queue2.size();
        }
        return 0;
    }

    /**
     * 實現一個只查看棧頂元素的操作
     * 思路也是先將q1的n-1個元素入隊q2,這時將q1中的剩餘元素peek出來,不是poll出來(元素不能刪除),再將其也導入到q2中
     * // 注意這裏如果直接用的是雙端隊列Deque 其實直接可以在不爲空的隊列中 peekLast = =
     */
    @SuppressWarnings("all")
    T top() {
        T top = null;
        if (!queue1.isEmpty()) {
          while (queue1.size() > 1) {
              queue2.offer(queue1.poll());
          }
          top = queue1.peek();
          // 再將其入隊至q2
            queue2.offer(queue1.poll());
        }

        if (!queue2.isEmpty()) {
            while (queue2.size()>1) {
                queue1.offer(queue2.poll());
            }

            top = queue2.peek();
            queue1.offer(queue2.poll());
        }

        return top;
    }




    public static void main(String[] args) {

        TwoQueueToStack<Integer> twoQueueToStack = new TwoQueueToStack<>();

        // 壓棧
        Stream.iterate(1, i -> i+1).limit(10).forEach(twoQueueToStack::push);

        // 查看棧頂的元素
        System.out.println("棧頂元素:" + twoQueueToStack.top());

        // 出棧
        int size = twoQueueToStack.size();
        for (int i = 0; i <size; i++) { // 注意這裏不能寫成 i < twoQueueToStack.size() 因爲循環中的pop操作會減少stack中的元素
            System.out.println("元素出棧" + twoQueueToStack.pop());
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章