Pila de implementación de Python y su aplicación en cuatro operaciones aritméticas

Defina la clase de pila:

class Stack(object) :

    # 初始化栈为空列表
    def __init__(self):
        self.items = []

    # 判断栈是否为空,返回 True 或 False
    def is_empty(self):
        return self.items == []

    # 压栈,添加新元素进栈
    def push(self,item):
        self.items.append(item)

    # 出栈,删除栈顶元素,并返回
    # 注:列表的pop()方法用于根据索引删除并返回被删除的元素,没有传入索引参数,默认删除最后一个元素
    def pop(self):
        return self.items.pop()

    # 返回栈顶元素
    def peek(self):
        return self.items[len(self.items)-1]

    # 返回栈的大小
    def size(self):
        return len(self.items)
	
if __name__ == "__main__" :
    '''
    测试
    '''
    stack = Stack() # 栈类的一个实例对象
    stack.push("hello")
    stack.push("world")
    stack.push("chang")
    print(stack.is_empty()) # False
    print(stack.size())     # 3
    print(stack.peek())     # chang
    print(stack.pop())      # chang
    print(stack.pop())      # world
    print(stack.pop())      # hello
    print(stack.is_empty()) # True

PD: Desde el proceso de definir la clase de pila, puede ver que una serie de operaciones de pila son en realidad operaciones de lista. Si no desea implementar funciones definiendo la clase de pila, puede usar directamente la lista para lograr


La evaluación de cuatro expresiones aritméticas de la aplicación de la pila:

Paso: Convierta la expresión infija en la expresión sufija y calcule el valor de una expresión aritmética mediante la expresión sufija

El concepto de expresión infija y expresión postfija:

Las cuatro expresiones aritméticas estándar que se utilizan habitualmente se denominan expresiones infijas, como "9 + (3-1) * 3 + 10/2", que se caracteriza por el operador en el medio del número;

La expresión de sufijo es una forma de poner el operador después del número. "9 3 1-3 * + 10 2 / +" es la forma de expresión de sufijo correspondiente a la expresión de infijo anterior. La expresión de sufijo tiene otra característica Es eliminar todos los paréntesis;

Las expresiones infijas pueden mostrar la relación de operación de manera muy intuitiva, lo cual es conveniente para el cálculo manual, pero si desea diseñar un programa de computadora para calcular esta expresión, se vuelve muy engorroso. No solo se debe considerar la prioridad de las cuatro operaciones aritméticas, sino también los paréntesis Influencia y expresión de sufijo, aunque no en forma de expresión, es un cálculo informático muy intuitivo y fácil .


Reglas para convertir expresiones infijas en expresiones postfijas:

La conversión de expresiones infijas a expresiones postfijas requiere el uso de pilas, donde las pilas se utilizan para almacenar símbolos de operación en lugar de valores.

Regla: Recorre cada número y símbolo en la expresión infija de izquierda a derecha. Si es un número, se generará y pasará a formar parte de la expresión del sufijo; si es un símbolo, hay dos casos:

  1. Cuando es un paréntesis, si es un paréntesis izquierdo, coloque directamente el paréntesis izquierdo en la pila, si es un paréntesis derecho, el elemento superior de la pila se abre y se muestra a su vez, hasta que se abre un paréntesis izquierdo (el paréntesis izquierdo no se envía a la expresión del sufijo)

  2. Cuando es un símbolo aritmético, si el símbolo superior de la pila es un paréntesis izquierdo, el símbolo aritmético se coloca directamente en la pila. Cuando el símbolo de la parte superior de la pila no es un paréntesis izquierdo, si la prioridad del símbolo de la operación es mayor que el símbolo de la operación de la parte superior de la pila, se insertará en la pila. Cuando el símbolo de la parte superior de la pila es menor o igual, el elemento de la parte superior de la pila aparecerá y aparecerá hasta que la pila esté vacía o la parte superior de la pila esté Hasta el paréntesis de apertura , y luego coloque este símbolo en la pila .

    Finalmente, los símbolos en la parte superior de la pila se extraen de la pila y se imprimen, y el resultado es la expresión de sufijo final.

Ejemplos:

Tomando la expresión infija " 9 + ( 3-1 ) * 3 + 10/2 " como ejemplo, los pasos para derivar su expresión de sufijo son los siguientes:
1. Inicializar una pila vacía

2. El primer número 9 se envía directamente a la expresión de sufijo y el segundo símbolo "+" se coloca en la pila. En este momento, la expresión de sufijo: 9 símbolos en la pila: +

3. El siguiente paréntesis de la izquierda se conecta a la pila y se emite el número 3. En este momento, la expresión del sufijo es: 9 3 Los elementos de la pila son: + (

4. El siguiente es el signo menos "-". Dado que la parte superior de la pila es el paréntesis izquierdo, se inserta directamente en la pila y se emite el número 1. En este momento, la expresión del sufijo: 9 3 1 El símbolo en la pila: + (-

5. El siguiente es el paréntesis derecho, el símbolo "-" en la parte superior de la pila sale de la pila y sale, y luego el paréntesis izquierdo en la parte superior de la pila sale de la pila, en este momento la expresión del sufijo: 9 3 1- el símbolo en la pila: +

6. El siguiente es el signo de multiplicación "*", que tiene una prioridad más alta que el signo más "+" en la parte superior de la pila. Se inserta en la pila y se muestra el número 3. En este momento, la expresión del sufijo: 9 3 1-3 El símbolo en la pila: + *

7. El siguiente es el signo más "+", que tiene una prioridad más baja que la parte superior de la pila *, la parte superior de la pila * se extrae de la pila y se imprime, y luego se compara con la nueva parte superior de la pila el signo más "+", la prioridad es la misma y el "+" superior aparece fuera de la pila. Y salida, la pila está vacía, deténgase aquí y luego coloque el signo más "+" en la pila . La expresión del sufijo en este momento es: 9 3 1-3 * + símbolos en la pila: +

8. Se emite el siguiente número 10 y la prioridad del signo de división "/" es mayor que la prioridad del signo más "+" en la parte superior de la pila. La expresión del sufijo en este momento es: 9 3 1-3 * + 10 La pila es: + /

9. Se emite el último número 2. Una vez finalizado el recorrido, los símbolos en la parte superior de la pila se extraen de la pila y se muestran para obtener la expresión de sufijo final: 9 3 1-3 * + 10 2 / +

假设我们已经创建好了一个栈类,直接使用……
以下代码为将中缀表达式转化为后缀表达式

def compare(op1,op2) :
    '''
    比较两个运算符的优先级,乘除运算的优先级比加减高
    op1优先级比op2高返回True,否则返回False
    '''
    return op1 in ["*","/"] and op2 in ["+","-"]

# 中缀表达式转化为后缀表达式
s = "9+(3-1)*3+10/2" # 中缀表达式
stack_opt = Stack()  # 栈 stack_opt 用于储存运算符号
i = 0
temp_expression = '' # temp_expression 是后缀表达式
while i < len(s) :   # 遍历中缀表达式

    if s[i].isdigit() :
        '''如果s[i]是数字,需要再判断一下它后边的是不是也是数字,例如数字 10 ,它是一个整体 10,不是1和0'''
        start = i
        while i + 1 < len(s) and s[i + 1].isdigit():
            i += 1
        temp_expression += s[start:i+1] + " " # 此处是加了一个空格, i为连续的数字字符中的最后一个数字字符的位置
        
    elif stack_opt.size() == 0 or stack_opt.peek() == "(" : # 栈为空,或者栈顶为左括号,操作符直接入栈stack_opt
        stack_opt.push(s[i])
        
    elif s[i] == "(" or compare(s[i],stack_opt.peek()):     # 当前操作符为左括号或者比栈顶操作符的优先级高,操作符直接入栈
        stack_opt.push(s[i])
        
    elif s[i] == ")" : # 遇见右括号,栈中的操作符逐一出栈并输出,直到遇到左括号,此时左括号也出栈,但是不写进表达式中
        while stack_opt.peek() != "(" :
            temp_expression += stack_opt.pop() + " "
        stack_opt.pop() # 左括号出栈
        
    else :
        while stack_opt.size() != 0 : # 当前符号比栈顶运算符的优先级低或者相等时,栈中的元素依次出栈并输出直到栈为空或者栈顶为左括号为止
            if stack_opt.peek() == "(" :
                break
            temp_expression += stack_opt.pop() + " "
        stack_opt.push(s[i]) # 注意出栈结束之后,当前的这个符号还要入栈
    i += 1

while stack_opt.size() : # 最后一步:将栈中剩余的运算符依次输出
    temp_expression += stack_opt.pop() + " "

print(temp_expression)
# 输出结果:9 3 1 - 3 * + 10 2 / + 

El resultado se calcula mediante la expresión sufijo:

El cálculo de expresiones de sufijo debe realizarse con la ayuda de pilas Nota: La pila utilizada al calcular expresiones infijas se utiliza para almacenar símbolos de operación, mientras que la pila de expresiones de sufijo almacena valores .

Regla: recorra cada número y símbolo en la expresión de izquierda a derecha y empuje la pila cuando encuentre un número, y cuando encuentre un símbolo, colocará los dos números en la parte superior de la pila para el cálculo y luego empujará el resultado del cálculo a la pila. El valor final en la pila es el resultado del cálculo.

Tome la expresión de infijo "9 + (3-1) * 3 + 10/2" y la expresión de sufijo "9 3 1-3 * + 10 2 / +" como ejemplo, el proceso de cálculo es el siguiente:

1. Inicialice una pila vacía

2. Las tres primeras expresiones de sufijo son todas números, por lo que 9, 3 y 1 se colocan en la pila por turno. En este momento, los elementos de la pila son (el elemento más a la izquierda es el elemento inferior): 9 3 1

3. El siguiente es el signo menos "-". En este momento, se saca 1 de la pila como segundo número y 3 se saca como primer número (debido a que los elementos de la pila son el primero en entrar, el último en salir, el primero en salir de la pila es la operación El segundo número, y luego el primer número en la operación es el que se saca de la pila), realice la operación de resta 3-1 = 2 y luego empújelo a la pila. En este momento, los elementos en la pila son: 9 2

4. El siguiente es el número 3, empújelo hacia la pila, los elementos de la pila son: 9 2 3

5. El siguiente es el signo de multiplicación "*". En este momento, 3 y 2 se extraen de la pila, y se realiza la operación de multiplicación 2 * 3 = 6, y el resultado se inserta en la pila. En este momento, los elementos de la pila son: 9 6

6. El siguiente es el signo más "+", 9 y 6 se extraen de la pila para la operación de suma, y ​​el resultado es 15. En este momento, los elementos de la pila son: 15

7. A continuación, los números 10 y 2 se colocan en la pila. En este momento, los elementos de la pila son: 15 10 2

8. El siguiente es el signo de división "/", 10 y 2 se extraen de la pila para la operación de división, y el resultado es 5 en la pila. En este momento, los elementos de la pila son: 15 5

9. El último es el signo más "+", 15, 5 se extrae de la pila para la operación de suma, el resultado 20 es el resultado de la operación de toda la expresión, que es coherente con el resultado del cálculo de la expresión infija

El resultado solo se puede calcular atravesando en orden, sin considerar el complicado procesamiento lógico de las cuatro reglas aritméticas, que es muy conveniente para el procesamiento por computadora.

我们已经创建好了一个栈类,并且得到了“9 +3 - 1) * 3 + 10 / 2”的后缀表达式“9 3 1 - 3 * + 10 2 / + ”,它带有空格
以下代码为计算后缀表达式

temp_expression = "9 3 1 - 3 * + 10 2 / + "
def calculate(num1,num2,operator) :
    ''' 根据运算符号operator计算结果并返回 '''
    if operator == "+" :
        return int(num1) + int(num2)
    elif operator == "-" :
        return int(num1)  - int(num2)
    elif operator == "*" :
        return int(num1)  * int(num2)
    else :
        return int(num1)  / int(num2)

def process(data,operator) :
    ''' data出栈两个数值,进行一次计算,并将运算结果入栈data '''
    num2 = data.pop()
    num1 = data.pop()
    data.push(calculate(num1,num2,operator))

nums = Stack() # 这个栈用来储存数值
j = 0
while j < len(temp_expression) :
    if temp_expression[j].isdigit() :
        '''如果是数字,需要再判断一下它后边的是不是也是数字,例如数字 10 ,它是一个整体 10,不是1和0'''
        start = j
        while j + 1 < len(temp_expression) and temp_expression[j + 1].isdigit():
            j += 1
        nums.push(temp_expression[start:j+1]) # 将数值存入栈中

    elif not temp_expression[j].isspace() : # 因为我们的后缀表达式中带有空格,所以在不是数字,也不是空格的情况下,就是运算符号了,此时开始运算
        process(nums,temp_expression[j])    # 将运算符作为第二个参数传入
    j += 1
    
print(nums.pop()) # 运算完成后,栈中只有一个元素,就是最终结果 20

Optimización e integración:

A través de la introducción anterior, el valor de una expresión aritmética de cuatro solo se puede obtener convirtiendo la expresión infija en una expresión sufija y luego calculando la expresión sufijo. Se utiliza una pila en cada uno de estos dos pasos. La pila utilizada para derivar la expresión de sufijo almacena símbolos de operación y paréntesis (solo el paréntesis izquierdo, sin paréntesis derecho), y la pila utilizada al calcular la expresión de sufijo almacena números. Los dos pasos se ejecutan por separado y uno tras otro, la expresión infija se recorre una vez y la expresión sufijo se recorre una vez. Al mismo tiempo, en el proceso de derivar la expresión de sufijo y calcular la expresión de sufijo de la expresión de infijo, es necesario operar en la cadena. Si los dos pasos se combinan y ejecutan al mismo tiempo, no solo se puede simplificar el código, sino que también se puede mejorar la eficiencia del cálculo. Todo el proceso solo necesita atravesar la expresión infija una vez para completar el cálculo.

Idea: utilice una pila de datos para almacenar los números de operación y una pila opte por almacenar los símbolos de operación. Recorra la expresión infija de izquierda a derecha y, si es un número, se insertará en la pila.
Si es un símbolo, el símbolo se pone directamente en la pila opta por los siguientes cuatro casos:

  • La pila está vacía;
  • La parte superior de la pila es el paréntesis izquierdo;
  • El símbolo es el corchete izquierdo;
  • La prioridad de este símbolo de operación es mayor que el símbolo en la parte superior de la pila.

Si es un paréntesis derecho, realice un paso de cálculo: saque un símbolo aritmético de opt, saque dos números de los datos, realice un cálculo y envíe el resultado a los datos . Repita este paso de cálculo hasta que la parte superior de la pila de opciones sea el paréntesis izquierdo, y luego haga estallar el paréntesis izquierdo; si la prioridad del símbolo es menor que el símbolo superior de la pila de opciones o igual que el símbolo de la parte superior de la pila, repita el mismo cálculo que antes Pasos, hasta que la pila de opciones esté vacía, si está a la mitad, el símbolo superior de la pila de opciones es un paréntesis izquierdo, el paso de cálculo se detiene. Una vez completado el recorrido de la expresión infija, los pasos de cálculo anteriores continúan hasta que la pila de opciones está vacía.

Mira el código optimizado del Gran Dios (no yo ~~~):

def compare(op1, op2):
    """
    比较两个运算符的优先级,乘除运算优先级比加减高
    op1优先级比op2高返回True,否则返回False
    """
    return op1 in ["*", "/"] and op2 in ["+", "-"]
 
def getvalue(num1, num2, operator):
    """
    根据运算符号operator计算结果并返回
    """
    if operator == "+":
        return num1 + num2
    elif operator == "-":
        return num1 - num2
    elif operator == "*":
        return num1 * num2
    else:  # /
        return num1 / num2
 
def process(data, opt):
    """
    opt出栈一个运算符,data出栈两个数值,进行一次计算,并将结果入栈data
    """
    operator = opt.pop()
    num2 = data.pop()
    num1 = data.pop()
    data.append(getvalue(num1, num2, operator))
 
def calculate(s):
    """
    计算字符串表达式的值,字符串中不包含空格
    """
    data = []  # 数据栈
    opt = []  # 操作符栈
    i = 0  # 表达式遍历索引
    while i < len(s):
        if s[i].isdigit():  # 数字,入栈data
            start = i  # 数字字符开始位置
            while i + 1 < len(s) and s[i + 1].isdigit():
                i += 1
            data.append(int(s[start: i + 1]))  # i为最后一个数字字符的位置
        elif s[i] == ")":  # 右括号,opt出栈同时data出栈并计算,计算结果入栈data,直到opt出栈一个左括号
            while opt[-1] != "(":
                process(data, opt)
            opt.pop()  # 出栈"("
        elif not opt or opt[-1] == "(":  # 操作符栈为空,或者操作符栈顶为左括号,操作符直接入栈opt
            opt.append(s[i])
        elif s[i] == "(" or compare(s[i], opt[-1]):  # 当前操作符为左括号或者比栈顶操作符优先级高,操作符直接入栈opt
            opt.append(s[i])
        else:  # 优先级不比栈顶操作符高时,opt出栈同时data出栈并计算,计算结果如栈data
            while opt and not compare(s[i], opt[-1]):
                if opt[-1] == "(":  # 若遇到左括号,停止计算
                    break
                process(data, opt)
            opt.append(s[i])
        i += 1  # 遍历索引后移
    while opt:
        process(data, opt)
    print(data.pop())
 
if __name__ == '__main__':
    exp = "(9+((3-1)*3+10/2))*2"
    calculate(exp)

Supongo que te gusta

Origin blog.csdn.net/weixin_43974265/article/details/104967276
Recomendado
Clasificación