设计一个数据结构来支持堆栈操作并找到最小值

Posted

技术标签:

【中文标题】设计一个数据结构来支持堆栈操作并找到最小值【英文标题】:Design a datastructure to support stack operations and to find minimum 【发布时间】:2011-01-14 02:22:36 【问题描述】:

面试题:设计一个具有以下特点的数据结构

推送数据 弹出最后插入的数据 [LIFO] 给出最低限度

以上所有操作的复杂度都应该是O(1)

【问题讨论】:

Stack with find-min/find-max more efficient than O(n)?的可能重复 【参考方案1】:

有一个更有创意的解决方案,不使用辅助堆栈。

假设我们要将一个数字 value 压入一个最小数字 min 的堆栈。如果value大于等于min,则直接压入数据栈。如果小于min,我们推送2**value* -min,更新minvalue因为推送了一个新的最小数量。

要流行起来怎么样?如果数据栈顶(记为top)大于等于min,我们直接弹出。否则数字 top 不是真正推送的数字。实际推送的数字存储为 min。弹出当前最小数后,我们需要恢复之前的最小数,即2**min*-top

现在让我们证明这个解决方案的正确性。当value大于等于min时,直接压入数据栈,不更新min。因此,当我们发现数据栈顶大于等于min时,可以直接pop而不更新min。但是,如果我们发现 value 小于 min,我们推送 2value*-min。我们应该注意到 2value*-min 小于 value。然后我们将当前的 min 更新为 value。因此,新的数据栈顶(top)小于当前的min。因此,当我们发现数据栈顶小于min时,真正的栈顶(实际推入数value)存储在min中.弹出数据栈顶后,我们要恢复之前的最小数。由于 top = 2**value*-previous min 并且 value 是当前 min,所以 pervious min 是 2*当前 min - top

C++ 示例代码如下所示:

template <typename T> class StackWithMin 
public:
    StackWithMin(void) 
    virtual ~StackWithMin(void) 

    T& top(void);
    void push(const T& value);
    void pop(void);
    const T& min(void) const;

private:
    std::stack<T>   m_data;     // data stack, to store numbers
    T               m_min;      // minimum number
;

template <typename T> void StackWithMin<T>::push(const T& value) 
    if(m_data.size() == 0) 
        m_data.push(value);
        m_min = value;
    
    else if(value >= m_min) 
        m_data.push(value);
    
    else 
        m_data.push(2 * value - m_min);
        m_min = value;
    


template <typename T> void StackWithMin<T>::pop() 
    assert(m_data.size() > 0);

    if(m_data.top() < m_min)
        m_min = 2 * m_min - m_data.top();

    m_data.pop();


template <typename T> const T& StackWithMin<T>::min() const 
    assert(m_data.size() > 0);

    return m_min;


template <typename T> T& StackWithMin<T>::top() 
    T top = m_data.top();
    if(top < m_min)
        top = m_min;

    return top;

这个解决方案是借自my blog,还有我的书《Coding Interviews: Questions, Analysis & Solutions》。

【讨论】:

【参考方案2】:

这个问题其实是求Heap

PriorityQueue 是一个经典案例(Heap 的实现)。见java.util.PriorityQueue

我希望有一种简单的方法可以在线参考 Java 语言源代码,在那里我可以看到并参考 PriorityQueue 类的实现。

【讨论】:

【参考方案3】:

您可以通过维护两个堆栈来做到这一点

stack - 在这个堆栈上执行通常的 push 和 pop 操作。

minStack - 此堆栈用于在O(1) 时间内获取堆栈中的 min ele。在任何时候,此堆栈的顶部元素将是堆栈中所有元素的最小值。

push( item a) 
    // push the element on the stack.
    stack.push(a)   

    // find the min of the ele to be pushed and the ele on top of minStack.
    if(minStack.isEmpty()) 
        min = a
    else
        min = Min(a,minStack.top())

    // push the min ele on the minStack.
    minStack.push(min) 
end push


pop()
    // pop and discard
    minStack.pop() 

    // pop and return
    return stack.pop() 
end pop


findMin()
    return minStack.top()
end findMin

在上述方案中,每次将一个元素压入栈中,都会在minStack上进行相应的压入。所以任何时候堆栈中的元素数量和minStack 都是相同的。我们可以通过将元素推送到minStack 来稍微优化它,前提是该元素小于当前最小值。

push( item a) 
    // push the element on the orginal stack.
    stack.push(a)   

    if(minStack.isEmpty())
            // if minStack is empty push.
            minStack.push(a) 
    // else push only if the element is less than or equal to the present min.
    else if(a <= minStack.top()) 
        minStack.push(a)
end push

pop()
    // pop from the stack
    ele = stack.top()     
    if(minStack.top() == ele)
            // pop only if the top element is ele.
        minStack.pop() 

    // return the popped element.
    return ele 
end pop

【讨论】:

我们可以。我们可以消除 minStack 并在主堆栈本身上执行它的 push/pop,但从技术上讲,它不再是一个堆栈。【参考方案4】:

为此,您的数据结构应包含两个堆栈。一个应该正常工作;另一个只包含看到的最后一个最小元素。当你压入一个元素时,如果它小于/等于第二个堆栈的顶部(或堆栈为空),也将它压入第二个堆栈。当你弹出一个元素时,如果它等于第二个堆栈的顶部,则也弹出第二个堆栈。

任何时候的最小值都是第二个堆栈的顶部。

【讨论】:

以上是关于设计一个数据结构来支持堆栈操作并找到最小值的主要内容,如果未能解决你的问题,请参考以下文章

优先队列和堆

优先队列和堆

数据结构与算法之深入解析“最小栈”的求解思路与算法示例

数据结构(C语言版) 栈和队列 算法设计Demo11

155. 最小栈

155. 最小栈