【问题标题】:Stack with find-min/find-max more efficient than O(n)?使用 find-min/find-max 堆栈比 O(n) 更有效?
【发布时间】:2011-10-31 08:07:46
【问题描述】:

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

  • Push,在堆栈顶部添加一个新元素,
  • pop,移除栈顶元素,
  • Find-Max,返回(但不删除)堆栈的最大元素,并且
  • Find-Min,返回(但不删除)堆栈的最小元素,并且

这种数据结构的最快实现是什么?我该如何用 Java 编写它?

【问题讨论】:

  • 这是作业吗?你需要使用堆栈吗?
  • 搜索最小值/最大值不是正常的堆栈操作。
  • @Jetti 是的,需要一个堆栈实现
  • @SyntaxT3rr0r:我不是故意的。我的意思是问堆栈是否真的有必要(因为最初的问题并没有真正强调这一点)。
  • @Syntax 模板有助于重新编写问题。最初的问题不清楚,早期的cmet当时是合适的。

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


【解决方案1】:

这是一个经典的数据结构问题。问题背后的直觉如下 - 最大值和最小值可以改变的唯一方法是将一个新值压入堆栈或从堆栈中弹出一个新值。鉴于此,假设在堆栈中的每个级别,您都跟踪堆栈中该点处或以下的最大值和最小值。然后,当您将新元素压入堆栈时,您可以通过将刚刚压入的新元素与当前最大值和最小值进行比较,轻松(在 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,每个对象都包含堆栈元素、最小值和最大值。您可以利用ArrayListLinkedList 轻松做到这一点。或者,您可以使用提供的 Java Stack 类,尽管 IIRC 由于此应用程序可能不需要同步,它有一些开销。

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

希望这会有所帮助!

编辑:如果您好奇,我个人有 a min-stack 和上述 min-queue 的 C++ 实现地点。希望这能展示出这在实践中的样子!

【讨论】:

  • @Techkriti- 我已经用一些提示更新了我的答案。我不想只给你答案,因为你在上面已经指出这是一个家庭作业问题,但我之前已经在 C++ 中实现过,而且非常简单。
  • @Techkriti 我认为您可能会考虑使用标准的 Java Stack 类。如果你能编程,那么上面的解释就是你所需要的。all 那么上面的解释就是你所需要的。
  • @Techkriti- 我认为你遗漏了一个重要的细节。您不会只在堆栈中存储最小/最大值的一份副本。相反,您存储多个副本,堆栈中的每一层都有一个副本。与其使用整数的 ArrayList,不如考虑使用对象的 ArrayList,每个对象都存储三元组(值、当前最小值、当前最大值)。
  • 这是一个很好的答案和解释,但还有一些优化空间。如果我们的堆栈很大,并且最小值/最大值不经常更改,我们最终会浪费大量空间来一遍又一遍地存储相同的信息。一个好的优化是使用第二个堆栈来跟踪最小值。当一个值 i 被压入时,如果它 pop() 时,如果弹出的值等于另一个堆栈的顶部,我们也从另一个堆栈中弹出。 min() 通过查看辅助堆栈的顶部进行操作。我们可以将相同的想法应用于max()
  • @Hengameh 不,你不需要假设。如果弹出的条件是> 而不是>=,那将是正确的。在您的示例中,我们将 8 从辅助堆栈中弹出,但辅助堆栈的顶部将保持 8,因为我们两次推送 8(并且我们没有推送 3,因为它不 >= 大于 8)。
【解决方案2】:

虽然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();
        }
    }
}

请注意,使用这种方法,minStackmaxStack 中的元素将非常少,从而节省空间。例如

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)     

【讨论】:

  • 很好的解决方案,谢谢,+1。你把'='用来处理重复,对吧?我认为没有 '=' 这种方法是行不通的。我对吗?比如这个示例中,4,5,8,3,8,如果我们需要弹出,我们将删除8,即'max',这是不正确的。
【解决方案3】:

回复可能为时已晚,但只是为了记录。这是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();

    }
}

堆栈类:

class Node {

        int data;
        int min;
        int max;

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

        public Node() {
            super();
        }
    }

【讨论】:

    【解决方案4】:

    使用链表:

    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;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-10-31
      • 1970-01-01
      • 2014-07-27
      • 1970-01-01
      • 2016-01-12
      • 2012-05-12
      • 1970-01-01
      • 2018-03-04
      • 1970-01-01
      相关资源
      最近更新 更多