【python数据结构与算法】栈(Stack)的python实现及其应用

栈(Stack)的python实现及其应用

  • 一、什么是线性数据结构
  • 二、什么是栈
  • 三、栈的python实现
  • 四、栈的应用1 简单括号匹配
  • 五、栈的应用2 进制转换
  • 六、栈的应用3 中缀转后缀表达式
  • 七、栈的应用4 后缀表达式求值
  • 小结

一、什么是线性数据结构

栈,队列,双端队列和列表都是有序的数据集合,其元素的顺序取决于添加或移除顺序。一旦某个元素被添加进来,它与前后元素的相对位置将保持不变。这样的数据集合经常被称为线性数据结构

线性数据结构可以看作有两端。这两端有时候被称作“左端”和“右端”,有时候也被称作“前端”和“后端”。当然,它们还可以被称作“顶端”和“底端”。名字本身并不重要,真正区分线性数据结构的是元素的添加方式和移除方式,尤其是添加操作和移除操作发生的位置。举例来说,某个数据结构可能只允许在一端添加新元素,有些则允许从任意一端移除元素。

二、什么是栈

要搞清楚这个概念,首先要明白”栈“原来的意思,如此才能把握本质。"栈“者,存储货物或供旅客住宿的地方,可引申为仓库、中转站,所以引入到计算机领域里,就是指数据暂时存储的地方,所以才有进栈、出栈的说法。

栈有时候也被称为“下推栈”,它是有序集合,添加操作和移除操作总发生在同一侧,即“顶端”,另外一端被称为“底端”。
栈中的元素离底端越近,代表在栈中间的时间越长,因此栈的底端具有非常重要的意义。最新加入栈的元素将被最先移除。**这种排序原则被称为LIFO(last-in first-out),即后进后出。**它提供了一种基于在集合中的时间来排序的方式。最近添加的元素靠近顶端,旧元素则靠近底端。

栈具有反转(元素)特性,栈的应用很多,比如浏览器的返回(后退back)按钮,利用了一个栈,存储浏览过的网页的URL,单击按钮式,最新加入栈的URL弹出。

三、栈的python实现

栈支持以下操作
Stack() 创建一个空栈。不需要参数,且会返回一个空栈。
push(item)将一个元素添加到栈的顶端。他需要一个参数item,且无返回值。 (即进栈)
pop()将栈顶端的元素移除。它不需要参数,但会返回顶端的元素,并且修改栈的内容。 (即出栈)
peek()返回栈顶端的元素,但是并不移除该元素。他不需要返回参数,也不会修改栈的内容。
isEmpty() 检查栈是否为空。它不需要参数,且会返回一个布尔值。
size() 返回栈中元素的数目。它不需要参数,且会返回一个整数。

class Stack:          # 定义一个栈
    def __init__(self):    # 初始化栈为空列表
        self.items = []
        
    def isEmpty(self):
        return self.items == []
    
    def push(self,item):
        self.items.append(item)   # append()尾段加入数据
        
    def pop(self):
        return self.items.pop()
    
    def peek(self):
        return self.items[len(self.items)-1]
    
    def size(self):
        return len(self.items)
class Stack:          # 定义一个栈  方法二 首端作为栈顶栈底
    def __init__(self):
        self.items = []
        
    def isEmpty(self):
        return self.items == []
    
    def push(self,item):        
        self.items.insert(0,item)
        
    def pop(self):
        return self.items.pop(0)  # 此方法pop(0)时间复杂度提高了
    
    def peek(self):
        return self.items[0]
    
    def size(self):
        return len(self.items)
s = Stack()
print(s.isEmpty())
s.push(4)
s.push("dog")
s.push(True)
print(s.peek())
print(s.pop())
print(s.peek())

【python数据结构与算法】栈(Stack)的python实现及其应用_第1张图片

四、栈的应用1 简单括号匹配

括号匹配必须遵循平衡原则。
首先,每个开括号要恰好对应一个闭括号。
其次,每对开闭括号要正确的嵌套。
正确的括号:(()()()), (((())))), (()((()()))
错误的括号:(((()),()))),(()()(()
对括号是否正确匹配的识别,是很多语言编译器的基础算法。

def matches(open, close):   # 进行匹配操作的匹配的函数
    opens = ("([{")         # 定义opens 包含三个左括号
    closers = (")]}")       # 定义closers 包含三个右括号
    return opens.index(open) == closers.index(close) # 返回值为bool值  判断栈顶元素top和取到的元素symbol在两个

def parChecker(symbolString):
    s = Stack()         # 生成一个空栈s
    balanced = True     # 初始化balanced为True
    index = 0           # index次序
    while index < len(symbolString) and balanced: # 当index次序小于字符串的长度并且目前为止是平衡时 依次取元素
        symbol = symbolString[index]   #取其中一个元素赋值给symbol
        if symbol in "([{":         # 如果是左括号
            s.push(symbol)          # 入栈
        else:                       # 如果是右括号
            if s.isEmpty():             # 先判断栈是否为空,如果为空的话
                balanced = False        # banlanced为False 说明栈进去了一个右括号,不平衡
            else:                       # 栈不为空,说明栈里面有左括号
                top = s.pop()           # 顶部元素(左括号)弹出,赋值给top
                if not matches(top,symbol):  # 利用matches函数进行比较,看弹出的top和此时取到的元素symbol是否相同
                    balanced = False         # 先整体考虑算法实现过程,具体怎么匹配之后再考虑(抽象的思想)
        index = index + 1                    # index加一,继续下一次操作

    if balanced and s.isEmpty():  # 最后判断 如果balanced为True 并且 栈最后为空,说明全部匹配成功(没有剩余,一一对应),返回True
        return True
    else:
        return False
print(parChecker("({})[]"))    # 测试语句
# 结果为True

五、栈的应用2 进制转换

数字进制转换
所谓进制,就是用多少个字符来表示整数。
十进制是0~9这十个数字字符,二进制是0,1两个字符。

# 除以2算法的python实现
def divideBy2(decNumber):
    remstack = Stack()       # 定义一个remstack变量,初始化为空栈
    
    while decNumber > 0:    
        rem = decNumber % 2          .
           # 变量rem存储所得到的余数  求余数
        remstack.push(rem)           # 求模   将求的的余数push()进栈
        decNumber = decNumber // 2   # 取余   整数除  继续除2  循环

    binString = ""               # 定义一个字符串存储要输出的数字
    while not remstack.isEmpty():     #  当栈非空时
        binString = binString + str(remstack.pop())    #  str()函数 返回一个值的字符串形式  pop()弹出栈顶元素

    return binString        # 返回值  函数返回binString 存储要输出的字符串  利用栈反转了次序
    
print(divideBy2(233))
# 数字进制转换 (暂时最高支持16进制),
def divideByN(decNumber,base):  # 需要进行转换的数字 多了一个参数base 转换为几进制
    digits = "0123456789ABCDEF" # digits(数字) 字符串形式存储
    remstack = Stack()          # 定义一个栈remstack
     
    while decNumber > 0:    
        rem = decNumber % base        # 定义rem变量 存储
        remstack.push(rem)           
        decNumber = decNumber // base    # 取余数

    newString = ""                  # 定义一个字符串存储要输出的数字
    while not remstack.isEmpty():   #  当栈非空时
        newString = newString + digits[remstack.pop()]    # 输出字符串   digits是字符串取字符串

    return newString    # 返回值
    
print(divideByN(233,4))
print(divideByN(233,8))
print(divideByN(233,16))

【python数据结构与算法】栈(Stack)的python实现及其应用_第2张图片

六、栈的应用3 中缀转后缀表达式

前序,中序,后序表达式
计算机需要使用完全括号表达式
比如A + B * C + D可以重新写成((A + (B * C) ) + D);
通过改变运算符和操作数的相对位置,可以得到前序表达式和后序表达式。
相互转化的方法
首先写出中序表达式全括号表达式,
将操作符替换至原本左括号的位置,得到前序表达式;
将操作符替换至原本右括号的位置,得到后序表达式;

中序 前序 后序
A + B * C      + A * B C    A B C * +
( A + B ) * C    * + A B C    A B + C *

中序转后序的通用算法
(1)创建用于保存运算符的空栈opstack,以及一个用于保存结果的空列表。
(2)使用字符串方法split将输入的中序表达式转换成一个列表。
(3)从左往右扫描这个标记列表。
a.如果标记是操作数,将其添加到结果列表的末尾。
b.如果标记是左括号,将其压人 opstack栈中。
c.如果标记是右括号,反复从opstack栈中移除元素,直到移除对应的左括号。将从取出的每一个运算符都添加到结果列表的末尾。
d.如果标记是运算符,将其压入opstack栈中。但是,在这之前,需要先从栈中取出优先级更高或相同的运算符,并将它们添加到结果列表的末尾。
(4)当处理完输入表达式以后,检查opstack。将其中所有残留的运算符全部添加到结果列表的末尾。

def infixToPostfix(infixexpr):
    prec = {
     }
    prec["*"] = 3   # 字典key 操作符 value 优先级的数字
    prec["/"] = 3   # 记录操作符优先级
    prec["+"] = 2
    prec["-"] = 2
    prec["("] = 1
    
    opStack = Stack()          # 定义opStack为空栈 暂存操作符
    postfixList = []           # 定义空列表postfixList   存储后缀表达式   最后的输出结果
    tokenList = infixexpr.split() # 将中缀表达式转换为单词(token)列表
    
    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]):# 栈不为空,且栈顶操作符优先级>=此token(单词) 进栈
                postfixList.append(opStack.pop())
            opStack.push(token)
            
    while not opStack.isEmpty():              # 栈内还有为输出的操作符,弹出到空表postfixList
        postfixList.append(opStack.pop())
    return " ".join(postfixList)

print(infixToPostfix("A * B + C * D"))       #  引号   空格分隔  大写字母或数字(前面规定)

上述代码最后的输出结果为
A B * C D * +

七、栈的应用4 后缀表达式求值

最后一个关于栈的例子是计算后序表达式
当扫描后序表达式时,需要保存操作数,而不是运算符。
换一个角度说当遇到一个运算符时,需要用离他最近的两个操作数来计算。

完整算法描述
假设后序表达式是一个以空格分隔的标记串。其中,运算符标记有*、/、+和-,操作数标记是一位的整数值。结果是一个整数。
(1)创建空栈operandstack。
(2)使用字符串方法split将输入的后序表达式转换成一个列表。
(3)从左往右扫描这个标记列表。
a.如果标记是操作数,将其转换成整数并且压入 operandstack栈中。
b.如果标记是运算符,从operandStack栈中取出两个操作数。第一次取出右操作数,第二次取出左操作数。进行相应的算术运算,然后将运算结果压人operandstack栈中。
(4)当处理完输入表达式时,栈中的值就是结果。将其从栈中返回。

为了方便运算,我们定义了辅助函数doMath。
它接受一个运算符和两个操作数,并进行相应的运算。

# 后缀表达式 求值
def postfixEval(postfixExpr):
    operandStack = Stack()          # 定义一个空栈 暂存操作数
    tokenList = postfixExpr.split() # 定义单词列表tokenList 存储分割后的单词
        
    for token in tokenList:         # 开始循环
        if token in "0123456789":   # 如果是操作数
            operandStack.push(int(token))      # 压入转化为 int类型 的单词
        else:
            operand2 = operandStack.pop()      # 先弹出栈顶的数字是右操作数
            operand1 = operandStack.pop()      # 后弹出栈顶的数字是左操作数
            result = doMath(token,operand1,operand2) # 结果利用doMath函数计算结果
            operandStack.push(result)          # 再将计算结果压入栈
        return operandStack.pop()              # 最后返回 栈弹出的数据  为最终结果

def doMath(op,op1,op2):        # 定义doMath函数 计算运算结果 需要两个运算数,一个运算符op
    if op == "*":
        return op1 * op2
    elif op == "/":
        return op1 / op2
    elif op == "+":
        return op1 + op2
    else:
        return op1 - op2
    
print(postfixEval('4 5 6 * +'))   # 中间用空格隔开
print(postfixEval('7 8 + 3 2 + /'))

上述代码最后的输出结果为
4
7

小结

介绍了栈的python实现及其简单应用,包括简单括号匹配,进制转换,中缀转后缀表达式,后缀表达式求值。

你可能感兴趣的:(python数据结构与算法,python,数据结构,栈)