[Datenstruktur] Verwendung des Stapels | Simulationsimplementierung | Anwendung | Der Unterschied zwischen Stapel und virtuellem Maschinenstapel und Stapelrahmen

Inhaltsverzeichnis

1. Stapeln

1.1 Konzept

1.2 Nutzung des Stacks

1.3 Simulationsimplementierung des Stapels

1.4 Stack-Anwendungsszenarien

1. Ändern Sie die Reihenfolge der Elemente

2. Konvertieren Sie die Rekursion in eine Schleife

3. Halterungsanpassung

4. Bewertung umgekehrter polnischer Ausdrücke

5. Passen Sie die Reihenfolge des Knallens und Drückens an

6. Mindeststapel

1.5 Konzeptunterscheidung


1. Stapeln

1.1 Konzept

Stapel : Eine spezielle lineare Liste, die das Einfügen und Löschen von Elementen nur an einem festen Ende ermöglicht . Ein Ende, an dem Dateneinfügungs- und -löschvorgänge ausgeführt werden, wird als oberer Teil des Stapels bezeichnet, und das andere Ende wird als unterer Teil des Stapels bezeichnet. Die Datenelemente im Stapel folgen dem LIFO-Prinzip (Last In First Out) (d. h. First In, Last Out ) .

Schieben des Stapels: Der Einfügevorgang des Stapels wird Push/Push/Push genannt, und die eingefügten Daten befinden sich oben auf dem Stapel.

Vom Stapel entfernen: Der Löschvorgang des Stapels wird als Entfernen vom Stapel bezeichnet. Ausgabedaten oben auf dem Stapel


1.2 Nutzung des Stacks

Methode Funktion
Stapel() Konstruieren Sie einen leeren Stapel
Und drücken(Und und) Schieben Sie e auf den Stapel und geben Sie e zurück
Und Pop() Entfernen Sie das oberste Element vom Stapel und geben Sie es zurück (mit Rückgabewert).
E peek() Holen Sie sich das oberste Element des Stapels
int size() Ermitteln Sie die Anzahl der gültigen Elemente im Stapel
boolean leer() Überprüfen Sie, ob der Stapel leer ist
public static void main(String[] args) {
        Stack<Integer> s = new Stack<>();
        s.push(1);
        s.push(2);
        s.push(3);
        s.push(4);
        System.out.println(s.size()); // 获取栈中有效元素个数---> 4
        System.out.println(s.peek()); // 获取栈顶元素---> 4
        s.pop(); // 4出栈,栈中剩余1 2 3,栈顶元素为3
        System.out.println(s.pop()); // 3出栈,栈中剩余1 2 栈顶元素为3
        if (s.empty()) {
            System.out.println("栈空");
        } else {
            System.out.println(s.size());
        }
    }

1.3 Simulationsimplementierung des Stapels

Wie Sie im Bild oben sehen können, erbt Stack Vector. Vector ähnelt ArrayList darin, dass es sich um eine dynamische Sequenzliste handelt. Der Unterschied besteht darin, dass Vector threadsicher ist.

Es gibt zwei Simulationsimplementierungen des Stapels: Eine ist eine Array-Implementierung und die andere ist eine Implementierung einer verknüpften Liste (einfach verknüpfte Liste oder doppelt verknüpfte Liste). Unabhängig davon, welche verwendet wird, muss die zeitliche Komplexität der Push- und Pop-Operationen berücksichtigt werden garantiert O(1) sein

So implementiert die Array-Simulation einen Stapel :

import java.util.Arrays;

//数组实现栈
public class MyStack {

    public int[] elem;//定义数组
    public int uesdSize;//记录当前数组的有效元素的个数,同时可以当作下标使用

    public static final int DEFAULT_CAPACITY = 10;//默认容量大小

    public MyStack() {
        this.elem = new int[DEFAULT_CAPACITY];
    }

    //判断栈是否满了
    public boolean isFull() {
        return uesdSize == elem.length;//这里不能写成DEFAULT_CAPACITY,DEFAULT_CAPACITY被final修饰不能变
    }
    //压栈 入栈
    public void push(int val) {
        if (isFull()) {
            this.elem = Arrays.copyOf(elem,2*elem.length);//扩容为原数组
        } else {
            elem[uesdSize++] = val;
        }
    }
    //判空
    public boolean isEmpty() {
        return uesdSize == 0;
    }
    //出栈
    public int pop() {
        if (isEmpty()) {
            throw new EmptyStackException("栈为空...");
        }
        int oldVal = elem[uesdSize-1];
        uesdSize--;
        elem[uesdSize] = 0;
        return oldVal;
    }
    //获取栈顶元素
    public int peek() {
        if (isEmpty()) {
            throw new EmptyStackException("栈为空...");
        }
        return elem[uesdSize-1];
    }
}

Wenn zur Implementierung des Stapels eine einseitig verknüpfte Liste verwendet wird , um sicherzustellen, dass die zeitliche Komplexität des Schiebens und Öffnens des Stapels O(1) beträgt

Beim Schieben auf den Stapel kann nur die Kopfeinfügungsmethode verwendet werden . Die Schwanzeinfügungsmethode erfordert das Durchlaufen der verknüpften Liste bis zum Endknoten, was der zeitlichen Komplexität von O (1) nicht gerecht wird.

Beim Entfernen aus dem Stapel kann nur die Methode zum Löschen des Kopfes verwendet werden . Möglicherweise möchten Sie last verwenden, um den Endknoten zu markieren, sodass Sie ihn nicht durchlaufen müssen. Nach dem einmaligen Löschen muss der Endknoten jedoch durchlaufen werden, um den Endknoten zu finden Vorheriger Knoten, der das Zeitlimit immer noch nicht einhält. Die Komplexität beträgt O (1).

Wenn zur Implementierung des Stapels eine doppelt verknüpfte Liste verwendet wird, ist es möglich, den Kopf in den Schwanz einzufügen.


1.4 Stack-Anwendungsszenarien

1. Ändern Sie die Reihenfolge der Elemente

1. Wenn die Push-Sequenz 1,2,3,4 ist und der Stapel während des Push-Vorgangs gepoppt werden kann , dann ist die folgende unmögliche Pop-Sequenz ()

A: 1,4,3,2 B: 2,3,4,1 C: 3,1,4,2 D: 3,4,2,1

2. Der Anfangszustand eines Stapels ist leer. Schieben Sie nun die Elemente 1, 2, 3, 4, 5, A, B, C, D und E nacheinander auf den Stapel und entfernen Sie sie dann nacheinander vom Stapel. Die Reihenfolge, in der die Elemente vom Stapel entfernt werden Ist ( ).

A: 12345ABCDE B: EDCBA54321 C: ABCDE12345 D: 54321EDCBA

Antwort:

1. Da die Eigenschaft des Stapels darin besteht, dass der Stapel zuerst reinkommt, wird bei Option C: Wenn 1, 2 und 3 alle auf den Stapel geschoben wurden, 3 vom Stapel genommen und dann ganz oben auf den Stapel gelegt ist 2. Es ist unmöglich, 1 direkt vom Stapel zu nehmen, also ist es falsch.

2. Was noch untersucht wird, ist, dass die Eigenschaft des Stapels „First in, last out“ ist. Die Elemente, die zuerst in den Stapel gelegt werden, werden zuletzt herausgenommen, also B

2. Konvertieren Sie die Rekursion in eine Schleife

Beispiel: Drucken Sie die verknüpfte Liste in umgekehrter Reihenfolge

// 递归方式
    void printList(Node head){
        if(null != head){
            printList(head.next);
        System.out.print(head.val + " ");
        }
    }

Die Methode der Schleife ähnelt hier der zweiten Frage oben. Die in den Stapel verschobenen und herausgesprungenen Elemente entsprechen der umgekehrten Reihenfolge. 

// 循环方式
    void printList(Node head){
        if(null == head){
            return;
        }
    Stack<Node> s = new Stack<>();
        // 将链表中的结点保存在栈中
        Node cur = head;
        while(null != cur){
            s.push(cur);
            cur = cur.next;
        }
        // 将栈中的元素出栈
        while(!s.empty()){
            System.out.print(s.pop().val + " ");
        }
    }
3. Halterungsanpassung

Überlegen Sie zunächst, warum diese Frage die Verwendung der Stapeldatenstruktur erfordert. Wann wird diese Datenstruktur verwendet?

Wenn es um die Reihenfolge geht, müssen Sie im Allgemeinen den Stapel berücksichtigen.

Die Idee dieser Frage:

Zunächst müssen Sie verstehen, dass diese Frage, wenn sie keine gerade Zahl ist, übereinstimmen muss, z. B.: [( ] )

 Solange es sich um eine linke Klammer handelt, wird sie auf den Stapel geschoben. Wenn sie auf eine rechte Klammer trifft, wird geprüft, ob sie übereinstimmt.

Die folgenden drei Situationen sind nicht übereinstimmend:

(1) Wenn die rechte Klammer nicht übereinstimmt, wird direkt false zurückgegeben. 

(2) Der String-Durchlauf wurde noch nicht abgeschlossen, aber der Stapel ist leer. Er stimmt zu diesem Zeitpunkt nicht überein. Beispiel: ())

(3) Der String-Durchlauf ist abgeschlossen, aber der Stapel ist nicht leer. Zu diesem Zeitpunkt gibt es keine Übereinstimmung. Beispiel: () (

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        //遍历字符串
        for(int i=0;i<s.length();i++) {
            char ch = s.charAt(i);
            if(ch=='{'||ch=='['||ch == '(') {
                //左括号入栈
                stack.push(ch);
            } else {
                //右括号
                if(stack.isEmpty()) {
                    //栈为空
                    return false;
                } 
                //栈不为空,右括号判断匹配
                 char ch2 = stack.peek();
                 if(ch2=='{'&&ch=='}'||ch2=='['&&ch==']'||ch2=='('&&ch==')') {
                 stack.pop();
                 } else {
                    return false;
                 }
            }
        }
        //遍历完了,但是栈不为空
        if(!stack.isEmpty()) 
            return false;
        return true;

         //return stcak.isEmpty() 可以直接代替前三行
    }
}

Beachten :

(1) Stack<Character> stack = new Stack<>(); Der Typ hier ist Character

  (2) ch2 ist die linke Klammer, ch ist die rechte Klammer

(3) Wie beurteilt man die Übereinstimmung? Ordnen Sie einfach eine Gruppe von Gruppen zu.


4. Bewertung umgekehrter polnischer Ausdrücke

Bevor wir uns mit dieser Frage befassen, lernen wir zunächst, was Präfix-, Mittel- und Suffixausdrücke sind, konvertieren Infixausdrücke in Suffixausdrücke und schauen uns schließlich an, wie Ergebnisse basierend auf Suffixausdrücken berechnet werden

(1) Infix-Ausdruck :

        Der Operator befindet sich in der Mitte der Operanden in Infixform (z. B. 3+2), was unser täglich gebräuchlicher Ausdruck arithmetischer und logischer Formeln ist.

(2) Suffixausdruck :

        Der Operator wird auch als Reverse Polish Notation (RPN) bezeichnet und befindet sich in der Suffixform nach den beiden Operanden (Beispiel: Die Suffixausdrucksform von 3+2 ist 3 2 +).

(3) Präfixausdruck:

        Der Operator wird auch als polnische Notation bezeichnet und vor den beiden Operanden in Präfixform platziert (Beispiel: Die Präfixausdrucksform von 3+2 ist + 3 2).

Wie konvertiere ich einen Infix-Ausdruck manuell in einen Postfix-Ausdruck?

Nehmen Sie als Beispiel a+b*c+(d*e+f)*g und konvertieren Sie es in einen Postfix-Ausdruck

(1) Fügen Sie dem Ausdruck zuerst Klammern nach dem Prinzip der Addition und Subtraktion und dann der Multiplikation und Division hinzu. Das Ergebnis ist ( (a+ ( b*c )+ (  (  ( d*e ) +f ) *g )

(2) Entfernen Sie die Klammern von innen nach außen und setzen Sie den Operator hinter die zu entfernenden Klammern, d. h. abc*+   de*f+ g* + 

Die Logik des Rechners ist wie folgt: Er wandelt den Ausdruck, den wir mit Klammern eingeben, in einen Ausdruck ohne Klammern um, da der Rechner nicht weiß, was Klammern sind.

 Der häufigste Codetest besteht hier darin, die Ergebnisse anhand von Suffixausdrücken zu berechnen . Was ist also die Idee?

Schieben Sie die Zahlen im Postfix-Ausdruck nacheinander auf den Stapel. Wenn ein Operand gefunden wird, platzieren Sie die beiden Elemente oben im Stapel.

Die erste Zahl wird als rechter Operand verwendet, die zweite Zahl wird als linker Operand verwendet und dann wird das berechnete Ergebnis von Nummer 2 und Operand Nummer 1 auf den Stapel gelegt (diese Reihenfolge kann nicht geändert werden).

Setzen Sie diesen Vorgang dann fort, bis nur noch das letzte Element im Stapel übrig ist, und kehren Sie direkt zurück.

Code:

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for(int i=0;i<tokens.length;i++) {
            String str = tokens[i];
           if(!isOperatons(str)) {
               //不是运算符,也就是数字
               //将字符串转为数字
               int val = Integer.valueOf(str);
               //将数字入栈
               stack.push(val);
           } else {
               //是运算符
                  //弹除两个栈顶元素,第一个为右操作数
               int num2 = stack.pop();
               int num1 = stack.pop();
                  //计算
               switch(str) {
                   case "+":
                        stack.push(num1+num2);
                        break;
                    case "-":
                        stack.push(num1-num2);
                        break; 
                   case "*":
                        stack.push(num1*num2);
                        break; 
                   case "/":
                        stack.push(num1/num2);
                        break;
               }
           }
        }
        return stack.pop();
    }
    //判断这个字符串是不是一个运算符
    private boolean isOperatons(String str) {
        if(str.equals("+")||str.equals("-")||str.equals("*")||str.equals("/")) {
            return true;
        } else {
            return false;
        }
    }
}

Beachten:

(1) Um die Zeichenfolge in den Stapel zu verschieben, muss die Zeichenfolge in eine Zahl umgewandelt werden int val = Integer.valueOf(str);

(2) Öffnen Sie die beiden obersten Elemente des Stapels. Das erste ist der rechte Operand und das zweite der linke Operand

5. Passen Sie die Reihenfolge des Knallens und Drückens an

 Idee:

(1) Durchlaufen Sie das pushV-Array und legen Sie die Elemente des pushV-Arrays in den Stapel

(2) Jedes Mal, wenn Sie ein Element einfügen, müssen Sie prüfen, ob es mit dem Element in popV identisch ist.

(3) Wenn sie unterschiedlich sind, ist i++ dasselbe, j++ und pop das oberste Element des Stapels (i durchläuft das pushA-Array, j durchläuft das popA-Array)

Endet, bis popV durchlaufen wird

Wie unten gezeigt, müssen wir, wenn das oberste Element des pushV-Stacks mit popV[j] übereinstimmt, das oberste Element des pushA-Stacks entfernen, andernfalls können wir nicht beurteilen, ob das nächste Element gleich ist.

public class Solution {
    public boolean IsPopOrder (int[] pushV, int[] popV) {
        Stack<Integer> stack = new Stack();
        int j =0;
        for(int i=0;i<pushV.length;i++) {
            stack.push(pushV[i]);
            //如果pushV栈顶元素和popV[j]一样
            while(!stack.isEmpty()&&j<popV.length&&stack.peek()==popV[j]) {
                j++;
                stack.pop();
            }
        }
        if(j<popV.length) {
            return false;
        }
        return true;

        //return j == popV.length;  //这里行可以代替前三行
        //return stack.isEmpty;   //或者这样写也行
    }
}

Hinweis: Wenn das oberste Element des pushV-Stapels mit popV[j] identisch ist, liegt der j-Index möglicherweise außerhalb der Grenzen  und  der Stapel wird leer gepoppt , sodass besondere Überlegungen erforderlich sind.

6. Mindeststapel

 Idee:

In dieser Frage kann ein Stapel nicht das kleinste Element erhalten (wenn sich das kleinste Element nicht oben auf dem Stapel befindet, entspricht die Zeitkomplexität nicht O(1), was gegen die Fragebedingungen verstößt), und erwägen Sie dann die Verwendung von zwei Stapeln

(1) Der normale Stapelstapel wird zum Speichern von Daten verwendet, und der minimale Stapelstapel minStack wird zum Speichern des kleinsten Elements verwendet.

(2) Gewöhnliche Stapel müssen Elemente enthalten

(3) Wenn Daten zum ersten Mal im Mindeststapel gespeichert werden, legen Sie sie direkt ab. Andernfalls müssen Sie sie vor dem Speichern mit dem obersten Element des Mindeststapels vergleichen. (Achten Sie hier besonders auf = . Siehe Das Bild zur Erklärung: Wenn zu diesem Zeitpunkt das rechte Wenn -3 nicht platziert ist, wenn der normale Stapel platzt und auch der minimale Stapel platzt, dann ist der Mindestwert nicht -3, sondern -2, was offensichtlich inkonsistent ist.)

class MinStack {
    Stack<Integer> stack;
    Stack<Integer> minStack;
    //构造方法:初始化两个栈
    public MinStack() {
        stack = new Stack<>();
        minStack = new Stack<>();
    }
    
    public void push(int val) {
        stack.push(val);
        //如果第一次放(也就是minStack为空),直接放即可
        if(minStack.isEmpty()) {
            minStack.push(val);
        } else {
            //不是第一次放,那就只有val<= minStack栈顶元素才可以放
            if(val<= minStack.peek()) {
                minStack.push(val);
            }
        }
    }
    
    public void pop() {
        //根据题目不用考虑空栈
           int val = stack.pop();
           //如果普通栈pop出的元素就是最小,那么minStack也需要pop
           if(minStack.peek()==val) 
           {
               minStack.pop();
           }
        
        
    }
    //获取栈顶元素
    public int top() {
        return stack.peek();
    }
    //获取最小值
    public int getMin() {
        return minStack.peek();
    }
}

1.5 Konzeptunterscheidung

Was ist der Unterschied zwischen Stack, Virtual Machine Stack und Stack Frame?

Stack : eine First-In-Last-Out-Datenstruktur

Stapel virtueller Maschinen : ein Teil des Speichers der JVM

Stapelrahmen : Wenn eine Methode aufgerufen wird, wird der Methode ein Teil des Speichers zugewiesen.


Das war's mit diesem Inhalt. Sie können gerne im Kommentarbereich oder in privaten Nachrichten kommunizieren. Wenn Sie denken, dass das, was der Autor geschrieben hat, gut ist oder Sie etwas daraus gewonnen haben, senden Sie mir bitte einen Drei-Wege-Link mit einem Klick. Vielen Dank!

おすすめ

転載: blog.csdn.net/qq_73017178/article/details/135067088