如何使用兩個堆棧實現隊列?

假設咱們有兩個堆棧而沒有其餘臨時變量。 java

是否能夠僅使用兩個堆棧「構造」隊列數據結構? node


#1樓

public class QueueUsingStacks<T>
{
    private LinkedListStack<T> stack1;
    private LinkedListStack<T> stack2;

    public QueueUsingStacks()
    {
        stack1=new LinkedListStack<T>();
        stack2 = new LinkedListStack<T>();

    }
    public void Copy(LinkedListStack<T> source,LinkedListStack<T> dest )
    {
        while(source.Head!=null)
        {
            dest.Push(source.Head.Data);
            source.Head = source.Head.Next;
        }
    }
    public void Enqueue(T entry)
    {

       stack1.Push(entry);
    }
    public T Dequeue()
    {
        T obj;
        if (stack2 != null)
        {
            Copy(stack1, stack2);
             obj = stack2.Pop();
            Copy(stack2, stack1);
        }
        else
        {
            throw new Exception("Stack is empty");
        }
        return obj;
    }

    public void Display()
    {
        stack1.Display();
    }


}

對於每一個enqueue操做,咱們添加到stack1的頂部。 對於每一個dequeue,咱們將stack1的內容清空到stack2中,並刪除堆棧頂部的元素。時間複雜度爲出隊的O(n),由於咱們必須將stack1複製到stack2。 enqueue的時間複雜度與常規堆棧相同 數據結構


#2樓

and . 隊列中的兩個堆棧定義爲和 。 app

通過排隊的元素老是被推入 ide

can be popped out since it is the first element inserted into queue when is not empty. Dequeue: 的頂部能夠彈出,由於它是不爲空時插入隊列的第一個元素。 is empty, we pop all elements from and push them into one by one. 當爲空時,咱們從彈出全部元素並將它們推入 。 . 隊列中的第一個元素被推入的底部。 . 彈出和推送操做後,它能夠直接彈出,由於它位於的頂部。 spa

如下是相同的C ++示例代碼: code

template <typename T> class CQueue
{
public:
    CQueue(void);
    ~CQueue(void);

    void appendTail(const T& node); 
    T deleteHead();                 

private:
    stack<T> stack1;
    stack<T> stack2;
};

template<typename T> void CQueue<T>::appendTail(const T& element) {
    stack1.push(element);
} 

template<typename T> T CQueue<T>::deleteHead() {
    if(stack2.size()<= 0) {
        while(stack1.size()>0) {
            T& data = stack1.top();
            stack1.pop();
            stack2.push(data);
        }
    }


    if(stack2.size() == 0)
        throw new exception("queue is empty");


    T head = stack2.top();
    stack2.pop();


    return head;
}

這個解決方案來自個人博客 。 個人博客網頁上提供了有關逐步操做模擬的更詳細分析。 隊列


#3樓

讓隊列實現爲q,用於實現q的棧是stack1和stack2。 內存

q能夠經過兩種方式實現: element

方法1(經過使enQueue操做成本高昂)

此方法確保新輸入的元素始終位於堆棧1的頂部,所以deQueue操做只是從stack1彈出。 要將元素放在stack1的頂部,使用stack2。

enQueue(q, x)
1) While stack1 is not empty, push everything from stack1 to stack2.
2) Push x to stack1 (assuming size of stacks is unlimited).
3) Push everything back to stack1.
deQueue(q)
1) If stack1 is empty then error
2) Pop an item from stack1 and return it.

方法2(經過使deQueue操做成本高昂)

在此方法中,在隊列操做中,新元素輸入stack1的頂部。 在去隊列操做中,若是stack2爲空,則將全部元素移動到stack2,最後返回stack2的頂部。

enQueue(q,  x)
 1) Push x to stack1 (assuming size of stacks is unlimited).

deQueue(q)
 1) If both stacks are empty then error.
 2) If stack2 is empty
   While stack1 is not empty, push everything from stack1 to stack2.
 3) Pop the element from stack2 and return it.

方法2確定比​​方法1好。方法1在enQueue操做中移動全部元素兩次,而方法2(在deQueue操做中)移動元素一次並僅在stack2爲空時移動元素。


#4樓

// Two stacks s1 Original and s2 as Temp one
    private Stack<Integer> s1 = new Stack<Integer>();
    private Stack<Integer> s2 = new Stack<Integer>();

    /*
     * Here we insert the data into the stack and if data all ready exist on
     * stack than we copy the entire stack s1 to s2 recursively and push the new
     * element data onto s1 and than again recursively call the s2 to pop on s1.
     * 
     * Note here we can use either way ie We can keep pushing on s1 and than
     * while popping we can remove the first element from s2 by copying
     * recursively the data and removing the first index element.
     */
    public void insert( int data )
    {
        if( s1.size() == 0 )
        {
            s1.push( data );
        }
        else
        {
            while( !s1.isEmpty() )
            {
                s2.push( s1.pop() );
            }
            s1.push( data );
            while( !s2.isEmpty() )
            {
                s1.push( s2.pop() );
            }
        }
    }

    public void remove()
    {
        if( s1.isEmpty() )
        {
            System.out.println( "Empty" );
        }
        else
        {
            s1.pop();

        }
    }

#5樓

我會在Go中回答這個問題,由於Go在其標準庫中沒有豐富的集合。

因爲堆棧實際上很容易實現,我想我會嘗試使用兩個堆棧來完成雙端隊列。 爲了更好地理解我是如何得出答案的,我將實現分爲兩部分,第一部分但願更容易理解,但它不完整。

type IntQueue struct {
    front       []int
    back        []int
}

func (q *IntQueue) PushFront(v int) {
    q.front = append(q.front, v)
}

func (q *IntQueue) Front() int {
    if len(q.front) > 0 {
        return q.front[len(q.front)-1]
    } else {
        return q.back[0]
    }
}

func (q *IntQueue) PopFront() {
    if len(q.front) > 0 {
        q.front = q.front[:len(q.front)-1]
    } else {
        q.back = q.back[1:]
    }
}

func (q *IntQueue) PushBack(v int) {
    q.back = append(q.back, v)
}

func (q *IntQueue) Back() int {
    if len(q.back) > 0 {
        return q.back[len(q.back)-1]
    } else {
        return q.front[0]
    }
}

func (q *IntQueue) PopBack() {
    if len(q.back) > 0 {
        q.back = q.back[:len(q.back)-1]
    } else {
        q.front = q.front[1:]
    }
}

它基本上是兩個堆棧,咱們容許堆棧的底部彼此操縱。 我還使用了STL命名約定,其中堆棧的傳統push,pop,peek操做具備前/後前綴,不管它們是指隊列的前面仍是後面。

上述代碼的問題在於它不能很是有效地使用內存。 實際上,它會不斷增加,直到你的空間不足爲止。 那真的很糟糕。 對此的修復是儘量簡單地重用堆棧空間的底部。 咱們必須引入一個偏移來跟蹤這個,由於Go中的切片一旦收縮就不能在前面生長。

type IntQueue struct {
    front       []int
    frontOffset int
    back        []int
    backOffset  int
}

func (q *IntQueue) PushFront(v int) {
    if q.backOffset > 0 {
        i := q.backOffset - 1
        q.back[i] = v
        q.backOffset = i
    } else {
        q.front = append(q.front, v)
    }
}

func (q *IntQueue) Front() int {
    if len(q.front) > 0 {
        return q.front[len(q.front)-1]
    } else {
        return q.back[q.backOffset]
    }
}

func (q *IntQueue) PopFront() {
    if len(q.front) > 0 {
        q.front = q.front[:len(q.front)-1]
    } else {
        if len(q.back) > 0 {
            q.backOffset++
        } else {
            panic("Cannot pop front of empty queue.")
        }
    }
}

func (q *IntQueue) PushBack(v int) {
    if q.frontOffset > 0 {
        i := q.frontOffset - 1
        q.front[i] = v
        q.frontOffset = i
    } else {
        q.back = append(q.back, v)
    }
}

func (q *IntQueue) Back() int {
    if len(q.back) > 0 {
        return q.back[len(q.back)-1]
    } else {
        return q.front[q.frontOffset]
    }
}

func (q *IntQueue) PopBack() {
    if len(q.back) > 0 {
        q.back = q.back[:len(q.back)-1]
    } else {
        if len(q.front) > 0 {
            q.frontOffset++
        } else {
            panic("Cannot pop back of empty queue.")
        }
    }
}

這是不少小功能,但其中有6個功能,其中3個只是另外一個的鏡像。

相關文章
相關標籤/搜索