如何使用两个堆栈实现队列?

落爺英雄遲暮 提交于 2020-02-25 20:42:11

假设我们有两个堆栈而没有其他临时变量。

是否可以仅使用两个堆栈“构造”队列数据结构?


#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 . 队列中的两个堆栈定义为和 。

经过排队的元素总是被推入

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. 当为空时,我们从弹出所有元素并将它们推入 。 . 队列中的第一个元素被推入的底部。 . 弹出和推送操作后,它可以直接弹出,因为它位于的顶部。

以下是相同的C ++示例代码:

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可以通过两种方式实现:

方法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个只是另一个的镜像。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!