[Structure des données] Utilisation de la pile | Implémentation de la simulation | Application | La différence entre la pile et la pile de machine virtuelle et le cadre de pile

Table des matières

1. Pile

1.1 Concept

1.2 Utilisation de la pile

1.3 Implémentation de la simulation de la pile

1.4 Scénarios d'application de pile

1. Changer la séquence des éléments

2. Convertir la récursion en boucle

3. Correspondance des supports

4. Évaluation des expressions polonaises inversées

5. Faites correspondre l'ordre de popping et de poussée

6. Pile minimale

1.5 Distinction conceptuelle


1. Pile

1.1 Concept

Pile : Une liste linéaire spéciale qui permet uniquement l'insertion et la suppression d'éléments à une extrémité fixe . Une extrémité où les opérations d'insertion et de suppression de données sont effectuées est appelée le haut de la pile et l'autre extrémité est appelée le bas de la pile. Les éléments de données de la pile suivent le principe LIFO (Last In First Out) (c'est-à-dire premier entré, dernier sorti ) .

Pousser la pile : L'opération d'insertion de la pile est appelée push/push/push, et les données insérées se trouvent en haut de la pile.

Sortir de la pile : L'opération de suppression de la pile est appelée sortir de la pile. Données de sortie au sommet de la pile


1.2 Utilisation de la pile

méthode Fonction
Empiler() Construire une pile vide
Et pousse (Et et) Poussez e sur la pile et retournez e
Et pop() Extrayez l'élément supérieur de la pile et renvoyez-le (avec la valeur de retour)
E coup d'oeil() Obtenez l'élément supérieur de la pile
taille int() Obtenez le nombre d'éléments valides dans la pile
booléen vide() Vérifiez si la pile est vide
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 Implémentation de la simulation de la pile

Comme vous pouvez le voir sur l'image ci-dessus, Stack hérite de Vector. Vector est similaire à ArrayList dans le sens où il s'agit d'une liste de séquences dynamique. La différence est que Vector est thread-safe.

Il existe deux implémentations de simulation de la pile : l'une est une implémentation de tableau et l'autre est une implémentation de liste chaînée (liste chaînée simple ou liste chaînée double). Quelle que soit celle utilisée, la complexité temporelle des opérations push et pop doit être garanti d'être O(1)

Voici comment la simulation de tableau implémente une pile :

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

Si une liste chaînée unidirectionnelle est utilisée pour implémenter la pile , alors afin de garantir que la complexité temporelle de la poussée et de l'éclatement de la pile est O(1)

Pousser sur la pile ne peut utiliser que la méthode d'insertion de tête.La méthode d'insertion de queue nécessite de parcourir la liste chaînée jusqu'au noeud final, ce qui ne répond pas à la complexité temporelle de O(1).

Sortir de la pile ne peut utiliser que la méthode de suppression de la tête.Vous souhaiterez peut-être utiliser last pour marquer le noeud de queue afin de ne pas avoir à le traverser.Cependant, après l'avoir supprimé une fois, le noeud de queue doit traverser pour trouver le nœud précédent, qui ne respecte toujours pas la limite de temps. La complexité est O(1)

Si une liste doublement chaînée est utilisée pour implémenter la pile, il est alors possible d'insérer la tête dans la queue.


1.4 Scénarios d'application de pile

1. Changer la séquence des éléments

1. Si la séquence push est 1,2,3,4 et que la pile peut être sautée pendant le processus push , alors la séquence pop impossible suivante est ()

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

2. L'état initial d'une pile est vide. Maintenant, poussez les éléments 1, 2, 3, 4, 5, A, B, C, D et E dans l'ordre sur la pile, puis retirez-les de la pile dans l'ordre. est ( ).

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

répondre:

1. Puisque la caractéristique de la pile est le premier entré, dernier sorti, dans l'option C : lorsque 1, 2 et 3 ont tous été poussés sur la pile, 3 est retiré de la pile, puis le haut de la pile est 2. Il est impossible de retirer directement 1 de la pile, c'est donc faux.

2. Ce qui est encore à l'étude, c'est que la caractéristique de la pile est "premier entré, dernier sorti". Les éléments qui sont placés en premier dans la pile sont sortis en dernier, c'est donc B

2. Convertir la récursion en boucle

Par exemple : imprimer la liste chaînée dans l’ordre inverse

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

La méthode de boucle ici est similaire à la deuxième question ci-dessus. Les éléments poussés dans la pile et sortis sont équivalents à l'ordre inverse. 

// 循环方式
    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. Correspondance des supports

Tout d'abord, réfléchissez aux raisons pour lesquelles cette question nécessite l'utilisation de la structure de données de la pile. Quand cette structure de données sera-t-elle utilisée ?

Généralement, si cela est lié à l'ordre, vous devez considérer la pile.

L'idée de​​cette question :

Tout d'abord, vous devez comprendre que si cette question n'est pas un nombre pair, il doit correspondre, par exemple : [( ] )

 Tant qu'il s'agit d'un support gauche, il est poussé sur la pile. Lorsqu'il rencontre un support droit, il est vérifié pour voir s'il correspond.

Les trois situations suivantes ne correspondent pas :

(1) Si le support droit ne correspond pas, il renverra directement false. 

(2) Le parcours de chaîne n'est pas terminé mais la pile est vide. Elle ne correspond pas à ce moment. Par exemple : ())

(3) Le parcours de chaîne est terminé mais la pile n'est pas vide. Il n'y a pas de correspondance pour le moment. Par exemple : () (

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() 可以直接代替前三行
    }
}

Avis :

(1) Stack<Character> stack = new Stack<>(); Le type ici est Character

  (2) ch2 est le support gauche, ch est le support droit

(3) Comment juger l'appariement ? Il suffit de faire correspondre un groupe de groupes.


4. Évaluation des expressions polonaises inversées

Avant d'examiner cette question, apprenons d'abord ce que sont les expressions de préfixe, de milieu et de suffixe, convertissons les expressions d'infixe en expressions de suffixe et voyons enfin comment calculer les résultats en fonction des expressions de suffixe.

(1) Expression infixe :

        L'opérateur est situé au milieu des opérandes sous forme infixe (comme : 3+2), qui est notre expression commune quotidienne des formules arithmétiques et logiques.

(2) Expression du suffixe :

        Également connu sous le nom de notation polonaise inversée (RPN), l'opérateur se trouve sous la forme du suffixe après les deux opérandes (par exemple : la forme d'expression du suffixe de 3+2 est 3 2 +)

(3) Expression de préfixe :

        Également connu sous le nom de notation polonaise, l'opérateur est placé devant les deux opérandes sous forme de préfixe (par exemple : la forme d'expression du préfixe de 3+2 est + 3 2)

Comment convertir manuellement une expression infixe en expression postfixe ?

Prenez a+b*c+(d*e+f)*g comme exemple, convertissez-le en une expression postfixe

(1) Ajoutez d'abord des parenthèses à l'expression selon le principe d'addition et de soustraction, puis de multiplication et de division , et le résultat est ( (a+ ( b*c )+ (  (  ( d*e ) +f ) *g )

(2) Retirez les supports de l'intérieur vers l'extérieur et placez l'opérateur après les supports à retirer, c'est-à-dire abc*+   de*f+ g* + 

La logique de la calculatrice est la suivante : elle convertira l'expression que nous saisissons avec des parenthèses en une expression sans parenthèses, car la calculatrice ne sait pas ce que sont les parenthèses.

 Le test de code le plus courant ici consiste à calculer les résultats en fonction d'expressions de suffixe . Alors, quelle est l'idée ?

Poussez les nombres de l'expression suffixe sur la pile dans l'ordre. Lorsqu'un opérande est rencontré, placez les deux éléments en haut de la pile.

Le premier nombre est utilisé comme opérande de droite, le deuxième nombre est utilisé comme opérande de gauche, puis le résultat calculé du numéro 2 et de l'opérande numéro 1 est placé sur la pile (cet ordre ne peut pas être modifié)

Continuez ensuite ce processus jusqu'à ce qu'il ne reste plus que le dernier élément dans la pile et revenez directement.

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

Avis:

(1) Pousser la chaîne dans la pile nécessite de changer la chaîne en un nombre int val = Integer.valueOf(str);

(2) Pop les deux éléments supérieurs de la pile, le premier est l'opérande de droite, le second est l'opérande de gauche

5. Faites correspondre l'ordre de popping et de poussée

 Idée:

(1) Parcourez le tableau pushV et placez les éléments du tableau pushV dans la pile

(2) Chaque fois que vous mettez un élément, vous devez vérifier s'il est identique à l'élément dans popV.

(3) S'ils sont différents, i++ est le même, j++ et fait apparaître l'élément supérieur de la pile (i traverse le tableau pushA, j traverse le tableau popA)

Terminer jusqu'à ce que popV soit traversé

Comme indiqué ci-dessous, lorsque l'élément supérieur de la pile pushV est le même que popV[j], nous devons faire apparaître l'élément supérieur de la pile pushA, sinon nous ne pouvons pas juger si l'élément suivant est égal.

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;   //或者这样写也行
    }
}

Remarque : lorsque l'élément supérieur de la pile pushV est le même que popV[j], l' indice j peut être hors limites  et  la pile sera vide , une attention particulière est donc requise.

6. Pile minimale

 Idée:

Dans cette question, une pile ne peut pas obtenir le plus petit élément (si le plus petit élément n'est pas en haut de la pile, alors la complexité temporelle ne répond pas à O(1), ce qui viole les conditions de la question), alors envisagez d'utiliser deux piles

(1) La pile ordinaire Stack est utilisée pour stocker les données et la pile minimale minStack est utilisée pour stocker le plus petit élément.

(2) Les piles ordinaires doivent contenir des éléments

(3) Si c'est la première fois que vous stockez des données dans la pile minimale, placez-les directement. Sinon, elles doivent être comparées avec l'élément supérieur de la pile minimale avant d'être enregistrées. (Faites particulièrement attention ici à = . Voir l'image pour expliquer : à ce moment, si le droit Si -3 n'est pas placé, lorsque la pile normale apparaît et que la pile minimale apparaît également, alors la valeur minimale ne sera pas -3, mais -2, ce qui est évidemment incohérent)

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 Distinction conceptuelle

Quelle est la différence entre la pile, la pile de machines virtuelles et le cadre de pile ?

Stack : une structure de données premier entré, dernier sorti

Pile de machine virtuelle : un morceau de mémoire de la JVM

Stack frame : Lorsqu’une méthode est appelée, un morceau de mémoire est alloué à la méthode.


C'est tout pour ce contenu. Vous êtes invités à communiquer dans la zone de commentaires ou dans les messages privés. Si vous pensez que ce que l'auteur a écrit est bon, ou si vous en avez tiré quelque chose, veuillez me donner un lien à trois voies en un clic. Merci beaucoup!

Je suppose que tu aimes

Origine blog.csdn.net/qq_73017178/article/details/135067088
conseillé
Classement