栈 数据结构与算法 python版

什么是栈?

一种有次序的数据项集合,在栈中,数据 项的加入和移除都仅发生在同一端 这一端叫栈“顶top”,另一端叫栈“底base”

距离栈底越近的数据项,留在栈中的时间 就越长 而最新加入栈的数据项会被最先移除。这种次序通常称为“后进先出LIFO”: Last in First out 这是一种基于数据项保存时间的次序,时间越短 的离栈顶越近,而时间越长的离栈底越近。

栈的特性:反转次序

这种访问次序反转的特性,我们在某些计 算机操作上碰到过 浏览器的“后退back”按钮,最先back的是最 近访问的网页 Word的“Undo”按钮,最先撤销的是最近操作。

抽象数据类型   Stack

抽象数据类型“栈”定义为如下的操作

Stack():创建一个空栈,不包含任何数据项

push(item):将item加入栈顶,无返回值

pop():将栈顶数据项移除,并返回,栈被修改

peek():“窥视”栈顶数据项,返回栈顶的数 据项但不移除,栈不被修改

isEmpty():返回栈是否为空栈

size():返回栈中有多少个数据项

用Python实现ADT Stack

在清楚地定义了抽象数据类型Stack之后 ,我们看看如何用Python来实现它?

Python的面向对象机制,可以用来实现 用户自定义类型 将ADT Stack实现为Python的一个Class 将ADT Stack的操作实现为Class的方法 由于Stack是一个数据集,所以可以采用Python 的原生数据集来实现,我们选用最常用的数据集 List来实现。

一个细节:Stack的两端对应list设置 可以将List的任意一端(index=0或者-1)设置 为栈顶 我们选用List的末端(index=-1)作为栈顶 这样栈的操作就可以通过对list的append和pop 来实现,很简单! 

# 用python实现ADT Stack    Chenyj
class Stack:
    def __init__(self):
        self.items=[]
    def isEmpty(self):
        return self.items==[]
    def push(self,item):
        self.items.append(item)
    def pop(self):
        return self.items.pop()
    def peek(self):
        return self.items[len(self.items)-1]
    def size(self):
        return len(self.items)

栈的应用:简单括号匹配

从左到右扫描括号串,最新打开的左括号,应该 匹配最先遇到的右括号 这样,第一个左括号(最早打开),就应该匹配 最后一个右括号(最后遇到) 这种次序反转的识别,正好符合栈的特性! 

 

# 括号匹配    Chenyj
from pythonds.basic.stack   import Stack
def parChecker(symbolString):
    s=Stack()
    balanced=True
    index=0
    while index< len(symbolString) and balanced:
          symbol=symbolString[index]
          if symbol=='(':
              s.push(symbol)
          else:
              if s.isEmpty():
                  balanced=False
              else:
                  s.pop()
          index =index+1
    if balanced and s.isEmpty():
        return True
    else:
        return False
print(parChecker('((()))'))
print(parChecker('(()'))

运行结果

 

 当然还可以举一反三    {}  []  都可以匹配

栈的应用:十进制转换为二进制

十进制转换为二进制,采用的是“除以2 求余数”的算法 将整数不断除以2,每次得到的余数就是由低到 高的二进制位。

“除以2”的过程,得到的余数是从低到 高的次序,而输出则是从高到低,所以需 要一个栈来反转次序。

# 括号匹配    Chenyj
from pythonds.basic.stack import Stack
def divideBy2(decNumber):
    remstack =Stack()
    while decNumber>0:
        rem=decNumber%2
        remstack.push(rem)
        decNumber=decNumber//2
    binString=''
    while not remstack.isEmpty():
        binString=binString+str(remstack.pop())
    return binString
print(divideBy2(42))

运行结果: 101010

十进制转换为二进制的算法,很容易可以 扩展为转换到任意N进制 只需要将“除以2求余数”算法改为“除以N求余 数”算法就可以。

栈的应用:表达式转换

中缀表达式

我们通常看到的表达式象这样:B*C,很 容易知道这是B乘以C

这种操作符(operator)介于操作数( operand)中间的表示法,称为“中缀” 表示法

 例如中缀表达式A+B 将操作符移到前面,变为“+AB” 或者将操作符移到最后,变为“AB+”。

我们就得到了表达式的另外两种表示法: “前缀”和“后缀”表示法 以操作符相对于操作数的位置来定义

无论表达式多复杂,需要转换成 前缀或者后缀,只需要两个步骤 将中缀表达式转换为全括号形式 将所有的操作符移动到子表达式所在的左括号( 前缀)或者右括号(后缀)处,替代之,再删除 所有的括号

 

通用的中缀转后缀算法:实例

 

 

# 用python实现ADT Stack    Chenyj
from pythonds.basic.stack import Stack
def infixToPostfix(infixexpr):
    prec={}
    prec['*'] = 3
    prec['/'] = 3
    prec['+'] = 3

    prec['-'] = 3
    prec['('] = 3
    opStack =Stack()
    postfixList=[]
    tokenList=infixexpr.split()
    for token in tokenList:
        if token in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' or token in '0123456789':
            postfixList.append(token)
        elif token=='(':
            opStack.push(token)
        elif token==')':
            topToken =opStack.pop()
            while topToken !='(':
                postfixList.append(topToken)
                topToken=opStack.pop()
        else:
            while (not opStack.isEmpty()) and \
              (prec[opStack.peek()]>=prec[token]):
                postfixList.append(opStack.pop())
            opStack.push(token)
    while not opStack.isEmpty():
        postfixList.append(opStack.pop())
    return ' '.join(postfixList)

 栈的应用:后缀表达式求值

作为栈结构的结束,我们来讨论“后缀表 达式求值”问题 ,跟中缀转换为后缀问题不同, 在对后缀表达式从左到右扫描的过程中, 由于操作符在操作数的后面, 所以要暂存操作数,在碰到操作符的时候 ,再将暂存的两个操作数进行实际的计算 仍然是栈的特性:操作符只作用于离它最近的两 个操作数。

后缀表达式求值:流程

创建空栈operandStack用于暂存操作数

将后缀表达式用split方法解析为单词( token)的列表

从左到右扫描单词列表:如果单词是一个操作数,将单词转换为整数int,压 入operandStack栈顶 如果单词是一个操作符(*/+-),就开始求值,从栈 顶弹出2个操作数,先弹出的是右操作数,后弹出的 是左操作数,计算后将值重新压入栈顶

单词列表扫描结束后,表达式的值就在栈顶

弹出栈顶的值,返回。

 

# 用python实现ADT Stack    Chenyj
from pythonds import Stack


def postfixEval(postfixExpr):
    operandStack = Stack()
    tokenList = postfixExpr.split()

    for token in tokenList:
        if token.isdigit():  # 使用 isdigit() 检查是否为数字
            operandStack.push(int(token))
        else:
            operand2 = operandStack.pop()
            operand1 = operandStack.pop()
            result = doMath(token, operand1, operand2)
            operandStack.push(result)

    return operandStack.pop()


def doMath(op, op1, op2):
    if op == '*':
        return op1 * op2
    elif op == '/':
        return op1 / op2
    elif op == '+':
        return op1 + op2
    elif op == '-':
        return op1 - op2
    else:
        raise ValueError(f"Invalid operator: {op}")  # 添加无效操作符的处理


# 示例
if __name__ == "__main__":
    expression = "3 4 + 2 * 7 /"  # 示例后缀表达式
    result = postfixEval(expression)
    print(result)  # 输出结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值