数据结构与算法Python版 栈的应用


一、栈的应用-十进制转换

十进制转换为二进制

  • 所谓的“进制”,就是用多少个字符来表示整数,十进制是0~9这十个数字字符,二进制是0、1两个字符
  • 十进制转换为二进制,采用的是“除以2求余数”的算法
  • “除以2”的过程,得到的余数是从低到高的次序,而输出则是从高到低,所以需要一个栈来反转次序

示例:十进制转换为二进制

def dec_to_bin(dec_number):
    s = Stack()
    while dec_number > 0:
        rem = dec_number % 2  # 取余
        s.push(rem)
        dec_number = dec_number // 2  # 取整

    result = ""
    while not s.is_empty():
        result += str(s.pop())

    return result

print(dec_to_bin(10))
print(dec_to_bin(63))


### 输出结果
1010
111111

扩展到更多进制转换

  • 二进制有两个不同数字0、1
  • 十进制有十个不同数字0、1、2、3、4、5、6、7、8、9
  • 八进制可用八个不同数字0、1、2、3、4、5、6、7
  • 十六进制的十六个不同数字则是0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F

示例:十进制转换为任意进制

def dec_to_n(dec_number, base):
    digits = "0123456789ABCDEF"
    s = Stack()
    while dec_number > 0:
        rem = dec_number % base  # 取余
        s.push(rem)
        dec_number = dec_number // base  # 取整

    result = ""
    while not s.is_empty():
        result += digits[s.pop()]

    return result

print(dec_to_n(10, 2))
print(dec_to_n(63, 2))
print(dec_to_n(63, 8))
print(dec_to_n(63, 16))


### 输出结果
1010
111111
77
3F

二、栈的应用-表达式转换

三种表达式

中缀表达式

  • A+B*C这种操作符(operator)介于操作数(operand)中间的表示法,称为“中缀”表示法
  • 但有时候中缀表示法会引起混淆,人们引入了操作符“优先级”的概念来消除混淆,同时引入了括号来表示强制优先级,括号的优先级最高,而且在嵌套的括号中,内层的优先级更高

前缀和后缀表达式

  • “前缀”和“后缀”表示法:以操作符相对于操作数的位置来定义
  • 中缀表达式A+B,将操作符移到前面,变为“+AB”,或者将操作符移到最后,变为“AB+”
  • 在前缀和后缀表达式中,操作符的次序完全决定了运算的次序,不再有混淆
中缀表达式infix前缀表达式prefix后缀表达式postfix
A + B * C + D++A * B C DA B C * + D +
(A + B) * (C + D)* + A B + C DA B + C D + *
A * B + C * D* + * A B C DA B * C D * +
A + B + C + D+++ A B C DA B + C + D +

中缀表达式转后缀表达式

分析

  • 中缀表达式A+B*C,其对应的后缀表达式是ABC*+。操作数ABC的顺序没有改变,操作符的出现顺序,在后缀表达式中反转了。由于*的优先级比+高,所以后缀表达式中操作符的出现顺序与运算次序一致。
  • 在中缀表达式转换为后缀形式的处理过程中,操作符比操作数要晚输出。所以在扫描到对应的第二个操作数之前,需要把操作符先保存起来。而这些暂存的操作符,由于优先级的规则,还有可能要反转次序输出。这种反转特性,使得我们考虑用栈来保存暂时未处理的操作符。
  • (A+B)*C,对应的后缀形式是AB+C*,这里+的输出比*要早,主要是因为括号使得+的优先级提升,高于括号之外的*。所以遇到左括号,要标记下,其后出现的操作符优先级提升了,一旦扫描到对应的右括号,就可
    以马上输出这个操作符。

算法描述

  • 从左到右扫描中缀表达式单词列表

    • 如果单词是操作数,则直接添加到后缀表达式列表的末尾
    • 如果单词是左括号“(”,则压入栈顶
    • 如果单词是右括号“)”,则反复弹出栈顶操作符,加入到输出列表末尾,直到碰到左括号
    • 如果单词是操作符“*/±”,则压入栈顶。但在压入之前,要比较其与栈顶操作符的优先级,如果栈顶的高于或等于它,就要反复弹出栈顶操作符,加入到输出列表末尾。直到栈顶的操作符优先级低于它。
  • 中缀表达式单词列表扫描结束后,把栈中的所有剩余操作符依次弹出,添加到输出列表末尾
    在这里插入图片描述

import string
from my_stack import Stack

def infix_to_postfix(infix_expr: str):
    # 运算符优先级
    priority_dict = {"(": 1, "+": 2, "-": 2, "*": 3, "/": 3}
    s = Stack()
    # 删除表达式中的所有空格,并转换为列表
    infix_list = list(infix_expr.replace(" ", ""))
    postfix_list = []
    for i in infix_list:
        if i in (string.ascii_letters + string.digits):
            postfix_list.append(i)
        elif i == "(":
            s.push(i)
        elif i == ")":
            while s.peek() != "(":
                postfix_list.append(s.pop())
        else:
            while not s.is_empty() and priority_dict[s.peek()] >= priority_dict[i]:
                postfix_list.append(s.pop())
            s.push(i)

    # 栈中剩余操作符依次弹出
    while not s.is_empty():
        postfix_list.append(s.pop())

    # 将列表拼接为字符串,并删除(符号
    return "".join(postfix_list).replace("(", "")


print(infix_to_postfix("A+B*C"))
print(infix_to_postfix("(A+B)*C"))
print(infix_to_postfix("A*B+C*D"))
print(infix_to_postfix("(A + B) * (C + D)"))
print(infix_to_postfix("A + B + C + D"))


### 输出结果
ABC*+
AB+C*
AB*CD*+
AB+CD+*
AB+C+D+

三、栈的应用-后缀表达式求值

分析

  • 在对后缀表达式从左到右扫描的过程中,由于操作符在操作数的后面,所以要暂存操作数,在碰到操作符的时候,再将暂存的两个操作数进行实际的计算。这仍然是栈的特性

  • 例如4 5 6 * +,先扫描到4、5两个操作数,但还不知道对这两个操作数能做什么计算。继续扫描,又碰到操作数6,继续暂存入栈。直到“*”,现在知道是栈顶两个操作数5、6做乘法。

  • 先弹出的是右操作数,后弹出的是左操作数,这个对于-和/很重要。为了继续后续的计算,需要把这个中间结果30压入栈顶,继续扫描后面的符号。当所有操作符都处理完毕,栈中只留下1个操作数,就是表达式的值。
    在这里插入图片描述

from my_stack import Stack

def do_math(op, num1, num2):
    # 进行+-*/运算
    return eval(num2 + op + num1)

def evaluate_postfix(expression: str):
    # 约定:后缀表达式expression是用空格隔开的一系列单词(token)构成
    expression_list = expression.split()
    s = Stack()
    
    for i in expression_list:
        if i in "+-*/":
            s.push(str(do_math(i, s.pop(), s.pop())))
        else:
            s.push(i)

    return s.pop()


print(evaluate_postfix("4 5 6 * +"))
print(evaluate_postfix("7 8 + 3 2 + /"))
print(evaluate_postfix("17 18 + 13 12 + *"))


### 运行结果
34
3.0
875

您正在阅读的是《数据结构与算法Python版》专栏!关注不迷路~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值