堆栈中的最小值

Posted

技术标签:

【中文标题】堆栈中的最小值【英文标题】:Min Value from Stack 【发布时间】:2009-09-04 04:26:20 【问题描述】:

我有一个包含一些整数数据的堆栈。我想在 O(1) 时间内从 Stack 中找出最小值。有什么想法吗?

PS:Stack 中的数据没有排序(增加/减少)。

谢谢,

纳温

【问题讨论】:

根据定义,堆栈中的数据顺序完全取决于您的推送方式。你需要检查所有元素 O(n)。如果对于推送的数据有一些更广为人知的事情,则可能存在异常。 相关,虽然我不确定我会说它是重复的。 ***.com/questions/1042507/… @GMan:给出“***.com/questions/1042507/…”的解决方案需要 O(n)。我的问题是,我们可以在 O(1) 中做到这一点吗? 【参考方案1】:

使用两个堆栈。一个是数据,一个是最小值。当您推入数据堆栈时,将新的最小值推入最小值堆栈(新的最小值是您正在推入的项目的最小值以及当前在最小值堆栈顶部的任何内容),当您弹出时,弹出两个堆栈(以便两个堆栈始终具有相同数量的元素)。要找到最小值元素,只需查看最小值堆栈的顶部即可。

推送、弹出和找到最小值是 O(1)。

【讨论】:

布拉沃 :-)。打败我……每个人都说这是不可能的! 我希望我能再次 +1 这个答案。我打赌你会从这个中获得一些徽章:-)。 一个更好的实现将最小堆栈保持在 1 个元素,不管有多少被添加到另一个元素,但这只是当你需要整个堆栈的最小值,而不是任何“子堆栈”在堆栈中。 @wrang wrang:在常规堆栈上推 1,在 minStack 上推 1。在 regularStack 上推 2,在 minStack 上推 1。在常规堆栈上推 3,在 minstack 上推 1 ...如果您弹出一次或两次(从两个堆栈中弹出),您仍然会在 minStack 顶部拥有正确的最小值。有意义吗? @Chris Lutz:你能澄清一下吗?您不能在最小堆栈中保留一项,因为这与将最小值存储在变量中相同......如果最小值从常规堆栈中弹出,您将不得不搜索新的最小值。跨度> 【参考方案2】:

O(n) 是你要做的最好的 - 你必须检查每个值并将它们与聚合器最小值进行比较,否则你怎么知道你得到了最低值?

如果您愿意,您可以在添加值时存储最小值,从而为 O(1) 读取(预先计算的最小值)带来更多成本,但仅此而已。

【讨论】:

堆栈中已经存在这些值。我只需要找出堆栈中的最小值。 @Naveen:答案还是一样的。堆栈上有 N 个值,您必须查看所有值以确定哪个是最小的。也就是 N 次比较,所以计算最多是 O(N)。 我相信你的意思是说:“让爆米花更贵”,因为在你的方法中,爆米花可能会导致你不得不搜索一分钟。但正如已经建议的那样,您可以使用两个堆栈。 另外,@Stephen C...你的意思是 N - 1 次比较...虽然仍然是 O(n) :-)。【参考方案3】:

根据定义,堆栈是push/pop (LIFO) 数据结构。您不能使用单个堆栈!

【讨论】:

实际上,如果您使用其他一些数据结构(例如第二个堆栈)来保存弹出的值,您可以在堆栈上找到最小值。但是 N 次弹出 + N 次比较 + N 次推送是 O(N)。 @Stephen 我说“你不能!”做“O(1)次”:)【参考方案4】:

我不确定您为什么希望在恒定时间内以任意长度执行此操作。你能做的最好的就是 O(n)

【讨论】:

是的,我知道它可以在 O(n) 时间内完成。我只想知道是否也可以在 O(1) 时间内完成。 他说,我引用,“你能做的最好的就是 O(n)。”所以不,你不能在 O(1) 中做到这一点。 这个答案并没有真正回答这个问题。它正在回答“在列表 n 个元素中找到最小值的大 O 是什么”的问题。它没有说明堆栈数据结构或如何修改它。 也许我在广场上想太多了;)但我严格地回答了这个问题“你能在恒定时间内找到堆栈的最小值吗”如果你只有那个堆栈,那么答案是否定的。【参考方案5】:

如果您想始终弹出最少的元素,您可能需要某种priority heap。如果您想弹出最后推送的内容,但能够知道堆栈中剩余元素的顺序,则可以使用某种搜索树,例如red-black 将支持从任意位置删除元素(您的堆栈将有一个指向树节点的指针,因此当您弹出时可以找到它)。

如果您只需要知道堆栈中剩余的最小值(或最大值),那么 ESRogs' 是最佳选择。

【讨论】:

【参考方案6】:

这是使用列表作为堆栈的 ESRogs 算法的 Python 实现:

class ConstantStack:
    def __init__(self):
        self.stack = []
        self.min_stack = []
    def push(self,item):
        self.stack.append(item)
        if len(self.min_stack) == 0:
            self.min_stack.append(item)
            return
        # Get the smaller item between the pushed item and the top of the stack
        smallest = min(item,self.min_stack[-1])
        self.min_stack.append(smallest)
    def pop(self):
        self.min_stack.pop()
        return self.stack.pop()
    def min(self):
        # NOTE: min_stack[-1] is equivalent to peek()
        return self.min_stack[-1]

下面是它的用法示例:

>>> s = ConstantStack()
>>> s.push(3)
>>> s.push(7)
>>> s.push(6)
>>> s.push(1)
>>> s.min()
1
>>> s.pop()
1
>>> # Now that 1 is gone, 3 is the next smallest
>>> s.min()
3
>>> s.pop()
6
>>> # 6 was popped but 3 still remains the smallest
>>> s.min()
3
>>> s.pop()
7
>>> s.min()
3
>>> s.pop()
3

【讨论】:

【参考方案7】:
#define STACKSIZE 50

typedef struct stack

    int item[STACKSIZE];
    int top;
MULSTACKEX;

void InitStack(MULSTACKEX &st)

   st.item[STACKSIZE] = 0;
   st.top = -1;


void Push(MULSTACKEX &st1, MULSTACKEX &st2, int elem)

    if(st1.top == -1)
       
        st1.top++;
        st1.item[st1.top] = elem;

        st2.top++;
        st2.item[st2.top] = elem;
    
    else
    
        st1.top++;
        st1.item[st1.top] = elem;

        if(elem < st2.item[st2.top])
        
            st2.top++;
            st2.item[st2.top] = elem;
        
    


void Display(MULSTACKEX &st1, MULSTACKEX &st2)

    cout<<"stack1 elements: "<<endl;
    for(int i = 0; i <= st1.top; i++)
    
        cout<<st1.item[i]<<"->";
    

    cout<<endl;
    cout<<"stack2 elements: "<<endl;
    for(int i = 0; i <= st2.top; i++)
    
        cout<<st2.item[i]<<"->";
    



int Pop(MULSTACKEX &st1, MULSTACKEX &st2)

    int elem = 0;
    if(st1.item[st1.top] == st2.item[st2.top])
    
        elem = st2.item[st2.top];
        st2.top--;

        elem = st1.item[st1.top];
        st1.top--;
    
    else
    
        elem = st1.item[st1.top];
        st1.top--;
    

    return elem;    

int FindMin(MULSTACKEX &st2)

    int elem = st2.item[st2.top];
    return elem;


int _tmain(int argc, _TCHAR* argv[])

    MULSTACKEX stack1, stack2;

    InitStack(stack1); 
    InitStack(stack2);


    Push(stack1,stack2,13); 
    Push(stack1,stack2,17);
    Push(stack1,stack2,5);
    Display(stack1,stack2);

    int min_elem1 = FindMin(stack2);
    cout<<"Min element in the list is: "<<min_elem1<<endl<<endl;

    int deletedelem2 = Pop(stack1,stack2);
    cout<<"Pop element from the stack:"<< deletedelem2 <<endl<<endl;
    Display(stack1,stack2);

    cout<<endl<<endl;

    Push(stack1,stack2,19);
    Push(stack1,stack2,8);
    Display(stack1,stack2);

    cout<<endl<<endl;

    int deletedelem1 = Pop(stack1,stack2);
    cout<<"Pop element from the stack:"<< deletedelem1 <<endl<<endl;
    Display(stack1,stack2);

    int min_elem2 = FindMin(stack2);
    cout<<"Min element in the list is: "<<min_elem2<<endl<<endl;

    return 0;

【讨论】:

【参考方案8】:

我们不是推送 1 个值,而是推送一对数字。第一个元素将是您要推送的元素,第二个元素将是堆栈的最小元素,我们应该跟踪最小元素。 (元素,minOfStack)

假设你有一个空数组。当你第一次将数据压入堆栈时,第一个元素也是最小值。

 data=[(1,1)]

然后添加值 2。检查堆栈的最小值 1, 1

data=[(1,1),(2,1)]

这里是python实现

class StackTrackingMin:
    def __init__(self):
        self._data=[]
    def push(self,x:int)->None:
        currentMin=self.getMin()
        if currentMin==None or currentMin>x:
            currentMin=x
        self._data.append((x,currentMin))
    def pop(self)->None:
        self._data.pop() # pop does not return anything
    def top(self)->int:
        return self._date[-1] if self._data else None
    def getMin(self)->int:
        return self._data[-1][1] if self._data else None

【讨论】:

以上是关于堆栈中的最小值的主要内容,如果未能解决你的问题,请参考以下文章

在堆栈中查找最小值 - 不会打印出任何结果

使用链表构建最小最大堆栈

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

12 带最小值操作的栈

如何使用数组来查找 C 中的最小值(可能还有最大值?)? [复制]

猫鼬中的最小和最大数量验证