[Estructura de datos] Uso de la pila | Implementación de simulación | Aplicación | La diferencia entre pila y pila de máquina virtual y marco de pila

Tabla de contenido

1. pila

1.1 Concepto

1.2 Uso de pila

1.3 Implementación de simulación de pila

1.4 Escenarios de aplicación de pila

1. Cambiar la secuencia de elementos.

2. Convertir la recursividad en un bucle.

3. Coincidencia de soportes

4. Evaluación de expresiones polacas inversas

5. Haga coincidir el orden de hacer estallar y empujar

6. Pila mínima

1.5 Distinción de conceptos


1. pila

1.1 Concepto

Pila : Una lista lineal especial que sólo permite la inserción y eliminación de elementos en un extremo fijo . Un extremo donde se realizan las operaciones de inserción y eliminación de datos se denomina parte superior de la pila y el otro extremo se denomina parte inferior de la pila. Los elementos de datos de la pila siguen el principio LIFO (último en entrar, primero en salir) (es decir, primero en entrar, último en salir ) .

Empujar la pila: la operación de inserción de la pila se llama empujar / empujar / empujar, y los datos insertados están en la parte superior de la pila.

Sacar de la pila: la operación de eliminación de la pila se llama sacar de la pila. Datos de salida en la parte superior de la pila


1.2 Uso de pila

método Función
Pila() Construir una pila vacía
Y empujar (Y y) Empuje e hacia la pila y devuelva e
Y pop() Saque el elemento superior de la pila y devuélvalo (con el valor de retorno)
E vistazo() Obtener el elemento superior de la pila
tamaño entero() Obtenga la cantidad de elementos válidos en la pila
booleano vacío() Compruebe si la pila está vacía
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 Implementación de simulación de pila

Como puede ver en la imagen de arriba, Stack hereda Vector. Vector es similar a ArrayList en que es una lista de secuencia dinámica. La diferencia es que Vector es seguro para subprocesos.

Hay dos implementaciones de simulación de la pila: una es una implementación de matriz y la otra es una implementación de lista vinculada (lista vinculada simple o lista vinculada doble). No importa cuál se use, la complejidad temporal de las operaciones push y pop debe Se garantiza que es O(1)

Así es como la simulación de matriz implementa una pila :

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 se utiliza una lista enlazada unidireccional para implementar la pila , entonces para garantizar que la complejidad temporal de empujar y abrir la pila sea O (1)

Empujar hacia la pila solo puede usar el método de inserción de cabeza . El método de inserción de cola requiere atravesar la lista vinculada hasta el nodo final, que no cumple con la complejidad temporal de O (1).

Salir de la pila solo puede usar el método de eliminación de la cabeza . Es posible que desees usar el último para marcar el nodo de la cola para que no tengas que atravesarlo. Sin embargo, después de eliminarlo una vez, el nodo de la cola tiene que atravesar para encontrar el nodo anterior, que aún no cumple con el límite de tiempo. La complejidad es O(1)

Si se utiliza una lista doblemente enlazada para implementar la pila, entonces es posible insertar la cabeza en la cola.


1.4 Escenarios de aplicación de pila

1. Cambiar la secuencia de elementos.

1. Si la secuencia de inserción es 1,2,3,4 y la pila se puede extraer durante el proceso de inserción , entonces la siguiente secuencia emergente imposible es ()

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

2. El estado inicial de una pila es vacío. Ahora empuje los elementos 1, 2, 3, 4, 5, A, B, C, D y E en la pila en secuencia, y luego sáquelos de la pila en secuencia. El orden en el que los elementos salen de la pila es ( ).

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

respuesta:

1. Dado que la característica de la pila es primero en entrar, último en salir, en la opción C: cuando 1, 2 y 3 han sido colocados en la pila, 3 sale de la pila y luego la parte superior de la pila. es 2. Es imposible sacar 1 directamente de la pila, por lo que está mal.

2. Lo que aún se está examinando es que la característica de la pila es primero en entrar, último en salir. Los elementos que se colocan primero en la pila salen últimos, por lo que es B.

2. Convertir la recursividad en un bucle.

Por ejemplo: imprima la lista vinculada en orden inverso

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

El método de bucle aquí es similar a la segunda pregunta anterior: los elementos insertados en la pila y sacados son equivalentes al orden inverso. 

// 循环方式
    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. Coincidencia de soportes

Primero, piense por qué esta pregunta requiere el uso de la estructura de datos de la pila: ¿cuándo se utilizará esta estructura de datos?

Generalmente, si está relacionado con el orden, es necesario considerar la pila.

La idea de esta pregunta:

Primero que nada, debes entender que si esta pregunta no es un número par, debe coincidir, por ejemplo: [( ] )

 Siempre que sea un corchete izquierdo, se empuja hacia la pila. Cuando encuentra un corchete derecho, se verifica para ver si coincide.

Las siguientes tres situaciones no coinciden:

(1) Si el paréntesis derecho no coincide, devolverá falso directamente. 

(2) El recorrido de la cadena no se ha completado pero la pila está vacía. No coincide en este momento. Por ejemplo: ())

(3) El recorrido de la cadena se completa pero la pila no está vacía. No hay coincidencias en este momento. Por ejemplo: () (

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

Aviso :

(1) Pila<Carácter> pila = nueva Pila<>(); El tipo aquí es Carácter

  (2) ch2 es el soporte izquierdo, ch es el soporte derecho

(3) ¿Cómo juzgar la coincidencia? Simplemente haga coincidir un grupo de grupos.


4. Evaluación de expresiones polacas inversas

Antes de ver esta pregunta, primero aprendamos qué son las expresiones de prefijo, medio y sufijo, conviertamos expresiones infijas en expresiones de sufijo y, finalmente, veamos cómo calcular resultados basados ​​en expresiones de sufijo.

(1) Expresión infija :

        El operador está ubicado en el medio de los operandos en forma infija (como: 3+2), que es nuestra expresión común diaria de fórmulas aritméticas y lógicas.

(2) Expresión de sufijo :

        También conocida como notación polaca inversa (RPN), el operador se ubica en la forma de sufijo después de los dos operandos (por ejemplo: la forma de expresión de sufijo de 3+2 es 3 2 +)

(3) Expresión de prefijo:

        También conocida como notación polaca, el operador se coloca delante de los dos operandos en forma de prefijo (por ejemplo: la forma de expresión de prefijo de 3+2 es + 3 2)

¿Cómo convertir manualmente una expresión infija en una expresión postfija?

Tome a+b*c+(d*e+f)*g como ejemplo y conviértalo en una expresión sufija

(1) Agregue paréntesis a la expresión de acuerdo con el principio de suma y resta primero, luego multiplicación y división , y el resultado es ( (a+ ( b*c )+ (  (  ( d*e ) +f ) *g )

(2) Retire los corchetes de adentro hacia afuera y coloque el operador después de los corchetes que se van a quitar, es decir, abc*+   de*f+ g* + 

La lógica de la calculadora es así: convertirá la expresión que ingresamos entre paréntesis en una expresión sin paréntesis, porque la calculadora no sabe qué son los paréntesis.

 La prueba de código más común aquí es calcular los resultados en función de expresiones de sufijo . Entonces, ¿cuál es la idea?

Empuje los números en la expresión sufijo en la pila en secuencia. Cuando encuentre un operando, coloque los dos elementos en la parte superior de la pila.

El primer número se usa como operando derecho, el segundo número se usa como operando izquierdo y luego el resultado calculado del número 2 y el operando número 1 se coloca en la pila (este orden no se puede cambiar).

Luego continúa este proceso hasta que solo quede el último elemento en la pila y regresa directamente.

Código:

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

Aviso:

(1) Empujar la cadena en la pila requiere cambiar la cadena a un número int val = Integer.valueOf(str);

(2) Extraiga los dos elementos superiores de la pila, el primero es el operando derecho, el segundo es el operando izquierdo

5. Haga coincidir el orden de hacer estallar y empujar

 Idea:

(1) Recorra la matriz pushV y coloque los elementos de la matriz pushV en la pila

(2) Cada vez que colocas un elemento, debes verificar si es el mismo que el elemento en popV.

(3) Si son diferentes, i++ es lo mismo, j++, y saca el elemento superior de la pila (i atraviesa la matriz pushA, j atraviesa la matriz popA)

Finalizar hasta que se atraviese popV

Como se muestra a continuación, cuando el elemento superior de la pila pushV es el mismo que popV [j], necesitamos extraer el elemento superior de la pila pushA; de lo contrario, no podemos juzgar si el siguiente elemento es igual.

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

Nota: Cuando el elemento superior de la pila pushV es el mismo que popV[j], el subíndice j puede estar fuera de los límites  y  la pila quedará vacía , por lo que se requiere una consideración especial.

6. Pila mínima

 Idea:

En esta pregunta, una pila no puede obtener el elemento más pequeño (si el elemento más pequeño no está en la parte superior de la pila, entonces la complejidad del tiempo no cumple con O (1), lo que viola las condiciones de la pregunta), entonces considere usar dos pilas

(1) La pila ordinaria Stack se usa para almacenar datos y la pila mínima minStack se usa para almacenar el elemento más pequeño.

(2) Las pilas ordinarias deben contener elementos.

(3) Si es la primera vez que almacena datos en la pila mínima, colóquelos directamente. De lo contrario, debe compararlos con el elemento superior de la pila mínima antes de guardarlos (preste especial atención aquí a = . Consulte La imagen para explicar: en este momento, si no se coloca -3 a la derecha, cuando la pila normal aparece y la pila mínima también aparece, entonces el valor mínimo no será -3, sino -2, lo que obviamente es inconsistente)

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 Distinción de conceptos

¿Cuál es la diferencia entre pila, pila de máquina virtual y marco de pila?

Pila : una estructura de datos de primero en entrar, último en salir

Pila de máquina virtual : una parte de la memoria de la JVM

Marco de pila : cuando se llama a un método, se asigna una parte de la memoria para el método.


Eso es todo por este contenido. Puedes comunicarte en el área de comentarios o mensajes privados. Si crees que lo que escribió el autor es bueno, o si has obtenido algo de ello, por favor dame un enlace de tres vías con un solo clic. ¡Muchas gracias!

Supongo que te gusta

Origin blog.csdn.net/qq_73017178/article/details/135067088
Recomendado
Clasificación