使用find-min / find-max进行堆栈比O(n)更有效?

时间:2011-08-20 19:24:22

标签: java algorithm data-structures stack big-o

我感兴趣的是创建一个类似于堆栈的Java数据结构,尽可能高效地支持以下操作:

  • 推送,在堆栈顶部添加新元素
  • Pop,删除堆栈的顶部元素
  • Find-Max,返回(但不删除)堆栈的最大元素,
  • Find-Min,返回(但不删除)堆栈的最小元素,

这个数据结构的最快实现是什么?我怎样才能用Java编写它?

4 个答案:

答案 0 :(得分:111)

这是一个经典的数据结构问题。问题背后的直觉如下 - 最大值和最小值可以改变的唯一方法是将新值推入堆栈或从堆栈中弹出新值。鉴于此,假设在堆栈中的每个级别,您可以跟踪堆栈中该点或以下的最大值和最小值。然后,当您将新元素推入堆栈时,您可以轻松地(在O(1)时间内)通过将刚刚推送的新元素与当前最大值和最小值进行比较来计算堆栈中任何位置的最大值和最小值。类似地,当你弹出一个元素时,你会将堆栈中的元素暴露在顶部下方一步,它已经在堆栈的其余部分中存储了最大值和最小值。

在视觉上,假设我们有一个堆栈并按顺序添加值2,7,1,8,3和9。我们从推2开始,所以我们将2推到我们的堆栈上。由于2现在也是堆栈中最大和最小的值,我们记录下来:

 2  (max 2, min 2)

现在,让我们推7.由于7大于2(当前最大值),我们最终得到这个:

 7  (max 7, min 2)
 2  (max 2, min 2)

请注意,现在我们可以通过查看堆栈顶部并看到7是最大值而2是最小值来读取堆栈的最大值和最小值。如果我们现在推1,我们得到

 1  (max 7, min 1)
 7  (max 7, min 2)
 2  (max 2, min 2)

这里,我们知道1是最小值,因为我们可以将1与存储在堆栈顶部的缓存最小值(2)进行比较。作为练习,请确保您了解为什么在添加8,3和9后,我们得到了这个:

 9  (max 9, min 1)
 3  (max 8, min 1)
 8  (max 8, min 1)
 1  (max 7, min 1)
 7  (max 7, min 2)
 2  (max 2, min 2)

现在,如果我们想查询最大值和最小值,我们可以在O(1)中通过读取堆栈顶部存储的最大值和最小值(分别为9和1)来实现。

现在,假设我们弹出顶部元素。这产生9并将堆栈修改为

 3  (max 8, min 1)
 8  (max 8, min 1)
 1  (max 7, min 1)
 7  (max 7, min 2)
 2  (max 2, min 2)

现在注意到这些元素的最大值是8,这正是答案!如果我们然后推0,我们就会得到这个:

 0  (max 8, min 0)
 3  (max 8, min 1)
 8  (max 8, min 1)
 1  (max 7, min 1)
 7  (max 7, min 2)
 2  (max 2, min 2)

而且,正如您所看到的,最大值和最小值都是正确计算的。

总的来说,这会导致堆栈的实现具有O(1)push,pop,find-max和find-min,它的渐进性与渐近一样好。我将把实施作为练习。 :-)但是,您可能需要考虑使用标准堆栈实现技术之一来实现堆栈,例如使用dynamic arraylinked list个对象,每个对象都包含堆栈元素min,最多您可以通过利用ArrayListLinkedList来轻松完成此操作。或者,您可以使用提供的Java Stack类,但是由于同步可能对此应用程序不必要,因此IIRC会产生一些开销。

有趣的是,一旦您构建了具有这些属性的堆栈,您就可以将其用作构建块来构建a queue with the same properties和时间保证。您还可以在更复杂的构造中使用它来构建具有这些属性的双端队列。

希望这有帮助!

编辑:如果你很好奇,我有 a min-stack 的C ++实现和前面提到的 min-queue 在我的个人网站上。希望这能够展示出在实践中看起来的样子!

答案 1 :(得分:30)

虽然answer是对的,但我们可以做得更好。如果堆栈有很多元素,那么我们就浪费了很多空间。但是,我们可以保存这个无用的空间,如下所示:

我们可以使用两个堆栈,而不是使用每个元素保存最小值(或最大值)。因为最小值(或最大值)的变化不会那么频繁,所以只有当新值为<=(或>=)时,我们才会将最小值(或最大值)推送到其各自的堆栈。当前最小(或最大)值。

以下是Java中的实施:

public class StackWithMinMax extends Stack<Integer> {

    private Stack<Integer> minStack;
    private Stack<Integer> maxStack;

    public StackWithMinMax () {
        minStack = new Stack<Integer>();    
        maxStack = new Stack<Integer>();    
    }

    public void push(int value){
        if (value <= min()) { // Note the '=' sign here
            minStack.push(value);
        }

        if (value >= max()) {
            maxStack.push(value);
        }

        super.push(value);
    }

    public Integer pop() {
        int value = super.pop();

        if (value == min()) {
            minStack.pop();         
        }

        if (value == max()) {
            maxStack.pop();         
        }

        return value;
    }

    public int min() {
        if (minStack.isEmpty()) {
            return Integer.MAX_VALUE;
        } else {
            return minStack.peek();
        }
    }

    public int max() {
        if (maxStack.isEmpty()) {
            return Integer.MIN_VALUE;
        } else {
            return maxStack.peek();
        }
    }
}

请注意,使用这种方法,minStack&amp;中的元素很少。 maxStack,从而节省空间。 e.g。

Stack : MinStack : MaxStack

7         7         7
4         4         7
5         1         8 (TOP)
6         1 (TOP)         
7
8                 
1                  
1                  
7
2
4
2 (TOP)     

答案 2 :(得分:2)

可能为时已晚,无法回复,只是为了记录。这是java代码。

import java.util.ArrayList;
import java.util.List;

public class MinStack {
    List<Node> items;

    public void push(int num) {
        if (items == null) {
            items = new ArrayList<Node>();
        }
        Node node = new Node(num);
        if (items.size() > 0) {
            node.min = Math.min(items.get(items.size() - 1).min, num);
            node.max = Math.max(items.get(items.size() - 1).max, num);

        } else {
            node.min = num;
            node.max = num;
        }
        items.add(node);
        printStack();
    }

    public Node pop() {
        Node popThis = null;
        if (items != null && items.size() > 0) {
            popThis = this.items.get(items.size() - 1);
            items.remove(items.size() - 1);         
        }
        printStack();
        return popThis;
    }

    public int getMin() {
        if (items != null && items.size() > 0) {
            int min = this.items.get(items.size() - 1).min;
            System.out.println("Minimum Element > " + min);
            return min;
        }
        return -1;
    }

    public int getMax() {
        if (items != null && items.size() > 0) {
            int max = this.items.get(items.size() - 1).max;
            System.out.println("Maximum Element > " + max);
            return max;
        }
        return -1;
    }

    public void printStack() {
        int i = 0;
        for (Node n : items) {
            System.out.print(n.data + " > ");
            if (i == items.size() - 1) {
                System.out.print(" | Min = " + n.min + " |");
                System.out.print(" | Max = " + n.max + " |");

            }
            i++;
        }
        System.out.println();
    }

    public static void main(String args[]) {
        MinStack stack = new MinStack();
        stack.push(10);

        stack.push(13);
        stack.push(19);
        stack.push(3);
        stack.push(2);
        stack.push(2);
        stack.printStack();
        stack.pop();
        //stack.getMin();
        stack.printStack();

    }
}

Stack Class:

class Node {

        int data;
        int min;
        int max;

        public Node(int data) {
            super();
            this.data = data;
        }

        public Node() {
            super();
        }
    }

答案 3 :(得分:0)

使用Linkedlist:

public class MaxMinStack {
    MaxMinLLNode headMin = null;
    MaxMinLLNode headMax = null;
    MaxMinLLNode tailMin = null;
    MaxMinLLNode tailMax = null;

    public void push(int data) {
        MaxMinLLNode node = new MaxMinLLNode(data, null);
        if (headMin == null) {
            headMin = node;
            tailMin = node;
        } else {
            if (data < headMin.data) {
                tailMin = headMin;
                headMin = node;
                node.nextNodeReference = tailMin;
            }
        }

        if (headMax == null) {
            headMax = node;
            tailMax = node;
        } else {
            if (data > headMax.data) {
                tailMax = headMax;
                headMax = node;
                node.nextNodeReference = tailMax;
            }
        }

    }

    public void pop() {
        System.out.println("Max Element:" + " " + String.valueOf(headMax.data));
        System.out.println("Min Element:" + " " + String.valueOf(headMin.data));
    }

    public void traverse() {
        MaxMinLLNode ptrMin = headMin;
        MaxMinLLNode ptrMax = headMax;
        System.out.println("Min");
        while (ptrMin != null) {
            System.out.println(ptrMin.data);
            ptrMin = ptrMin.nextNodeReference;
        }

        System.out.println("Max");
        while (ptrMax != null) {
            System.out.println(ptrMax.data);
            ptrMax = ptrMax.nextNodeReference;
        }

    }

    public static void main(String[] args) {
        MaxMinStack m = new MaxMinStack();
         m.push(7);
         m.push(4);
         m.push(5);
         m.push(6);
         m.push(7);
         m.push(8);
         m.push(1);
         m.push(1);
         m.push(7);
         m.push(2);
         m.push(4);
         m.push(2);
         m.traverse();
         m.pop();
    }

}

class MaxMinLLNode {
    int data;
    MaxMinLLNode nextNodeReference;

    MaxMinLLNode(int data, MaxMinLLNode node) {
        this.data = data;
        this.nextNodeReference = node;
    }
}