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

时间:2010-02-07 14:56:52

标签: algorithm data-structures stack

面试问题:设计具有以下功能的数据结构

  • 推送数据
  • 弹出最后插入的数据[LIFO]
  • 给出最低要求

以上所有操作都应具有O(1)

的复杂性

4 个答案:

答案 0 :(得分:13)

您可以通过维护两个堆栈

来完成此操作

stack - 在此堆栈上执行常规的推送和弹出操作。

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

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

这个问题实际上是要求Heap

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

我希望在线提供一种简单的方法来引用Java语言源代码,我可以看到它并参考PriorityQueue类的实现。

答案 3 :(得分:0)

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

假设我们要将数字推送到最小数量 min 的堆栈中。如果 value 大于或等于 min ,则会将其直接推送到数据堆栈中。如果它小于 min ,我们按2 **值* - min ,并将 min 更新为因为推出了新的最小数量。

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

现在让我们展示一下这个解决方案的正确性。当大于或等于 min 时,它将直接推送到数据堆栈而不更新 min 。因此,当我们发现数据堆栈的顶部大于或等于 min 时,我们可以直接弹出而不更新 min 。但是,如果我们发现 value 小于 min ,我们会推送2 值* - min 。我们应该注意到2 值* - min 小于。然后我们将当前 min 更新为。因此,新的数据堆栈顶部( top )小于当前的 min 。因此,当我们发现数据堆栈的顶部小于 min 时,真正的顶部(实际推送数)存储在 min 中。在我们弹出数据堆栈的顶部之后,我们必须恢复之前的最小数量。由于 top = 2 **值* - 以前 min 是当前 min ,因此 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”。