web-dev-qa-db-ja.com

O(n)よりも効率的なfind-min / find-maxでスタックしますか?

次の操作を可能な限り効率的にサポートするスタックに似たJavaデータ構造を作成することに興味があります。

  • プッシュ、スタックの上に新しい要素を追加し、
  • スタックの一番上の要素を削除するポップ
  • Find-Max。スタックの最大要素を返します(削除はしません)。
  • Find-Min。スタックの最小要素を返します(削除はしません)。

このデータ構造の最速の実装は何ですか? Javaで作成するにはどうすればよいですか?

50
Techkriti

これは古典的なデータ構造の質問です。問題の背後にある直感は次のとおりです。最大値と最小値を変更できる唯一の方法は、新しい値をスタックにプッシュするか、スタックから新しい値をポップすることです。これを前提として、スタックの各レベルで、スタックのそのポイント以下の最大値と最小値を追跡するとします。次に、新しい要素をスタックにプッシュすると、プッシュした新しい要素を現在の最大値と比較することにより、スタックの任意の場所で最大値と最小値を(O(1)時間で)簡単に計算できます。最小。同様に、要素からポップすると、スタック内の要素を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 array または linked list を使用するなど、標準のスタック実装手法のいずれかを使用してスタックを実装することを検討することもできます。スタック要素、最小値、最大値を保持します。 ArrayListまたはLinkedListを活用することで、これを簡単に行うことができます。あるいは、提供されたJava Stackクラスを使用できますが、IIRCには、このアプリケーションには不要な同期によるオーバーヘッドがあります。

興味深いことに、これらのプロパティを使用してスタックを構築したら、それを構築ブロックとして使用して、 同じプロパティを持つキュー と時間保証を構築できます。また、より複雑な構造で使用して、これらのプロパティを使用して両端キューを作成することもできます。

お役に立てれば!

EDIT:興味があれば、a min-stack のC++実装があります=および前述のmin-queue個人用サイトうまくいけば、これが実際にどのように見えるかを明らかにします!

113
templatetypedef

answer は正しいですが、より良い方法があります。スタックに多くの要素がある場合、多くのスペースを無駄にしています。ただし、次のようにしてこの無駄なスペースを節約できます。

各要素でmin(またはmax)値を保存する代わりに、2つのスタックを使用できます。最小(または最大)値の変更はそれほど頻繁ではないため、新しい値が_<=_(または_>=_)である場合にのみ、min(またはmax)値をそれぞれのスタックにプッシュします現在の最小(または最大)値。

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)     
_
31
Vasu

返信するには遅すぎるかもしれませんが、記録のためだけです。 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();
        }
    }
2
Sanjay Kumar

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;
    }
}
0
Nikita Gupta