栈的几个实例应用

简单括号匹配

问题描述:括号的使用必须遵循 “平衡”规则,即
首先,每个开括号要恰好对应一个闭括号;
其次,每对开闭括号要正确的嵌套
正确的括号:(()()()()),(((()))),
(()((())()))
错误的括号:((((((()),())),(()()(()
对括号是否正确匹配的识别,是很多语言编译器的基础算法

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

步骤:
1.创造一个空栈
2.若为左括号,则加入栈顶;若为右括号,则判断栈是否为空,若空,则无与右括号匹配的左括号,匹配失败,反之,则与栈顶的左括号匹配,将栈顶的左括号pop。(实现最新打开的左括号,应该匹配最先遇到的右括号
3.当括号串为空时,判断栈是否为空,若空,则匹配成功,反之,匹配失败。

demo:

from stack import Stack
#Stack是自定义栈类
def ParChecker(symbolstr):

    s = Stack()
    s_list = list(symbolstr)
    print(len(s_list))
    last = 0
    for string in s_list:
        last += 1
        print(last)
        if string == '(':
            s.push(string)
            print('(')
        elif string == ')':
            print(')')
            if s.isEmpty():
                print('error1')
                return False
            else:
                s.pop()

        if last == len(s_list):

            if s.isEmpty():
                return True
            else:
                print('error2')
                return False

print(ParChecker('((())'))

通用括号匹配

from stack import Stack
#Stack是自定义栈类
def ParChecker(symbolstr):

    s = Stack()
    s_list = list(symbolstr)
    opens = '([{'
    closers = ')]}'
    last = 0
    for string in s_list:
        last += 1

        if string in opens:
            s.push(string)
        elif string in closers:
            if s.isEmpty():
                print('error1')
                return False
            else:
            #当右括号类型与栈顶括号类型相同时,移除栈顶
                top = s.pop()
                if closers.index(string) != opens.index(top):
                    return False

        if last == len(s_list):
            if s.isEmpty():
                return True
            else:
                print('error2')
                return False

print(ParChecker('(([]))}'))

十进制转换为二进制

问题描述:
所谓的“进制”
,就是用多少个字符来表
示整数
十进制是0~9这十个数字字符,二进制是0、1两
个字符
我们经常需要将整数在二进制和十进制之间转换
十进制转换为二进制,采用的是“除以2
求余数”的算法
将整数不断除以2,每次得到的余数就是由低到
高的二进制位

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

思路:
将十进制整数除以2,若余数不为0,则压入栈顶;得到的栈再取出得到二进制

demo:

from stack import Stack
#Stack是自定义栈类

def tenTotwo(num):
    s = Stack()
    while num != 0:
        #转换为二进制,故除以2
        s.push(num % 2)
        num = num // 2

    string = ''
    while not s.isEmpty():
        string = string + str(s.pop())
    print(s.items)
    return string

print(tenTotwo(33))

转换成其他进制,只需改变除数即可

表达式转换

问题描述:
中缀表达式
BC,这种操作符(operator)介于操作数(
operand)中间的表示法,称为“中缀”表示法
但有时候中缀表示法会引起混淆,如
“A+B
C”
是A+B然后再乘以C
还是BC然后再去加A?
前缀、中缀和后缀表达式
❖再来看中缀表达式“(A+B)C”
,按照转换的规则,前缀表达式是“
+ABC”,而后缀表达式是“AB+C

前缀和后缀表达式
❖例如中缀表达式A+B
将操作符移到前面,变为“+AB”
或者将操作符移到最后,变为“AB+”
❖我们就得到了表达式的另外两种表示法:
“前缀”和“后缀”表示法
以操作符相对于操作数的位置来定义

❖在中缀表达式里必须
的括号,在前缀和后缀表达式中消失了?
❖在前缀和后缀表达式中,操作符的次序完
全决定了运算的次序,不再有混淆

所以在很多情况下,表达式的计算机表示都避免
用复杂的中缀形式

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

流程:
前提限制:后面的算法描述中,约定中缀表达式是由
空格隔开的一系列单词(token)构成,
操作符单词包括
/±()
而操作数单词则是单字母标识符A、B、C等。
*
❖首先,创建空栈opstack用于暂存操作符
,空表postfixList用于保存后缀表达式
❖将中缀表达式转换为单词(token)列表
从左到右扫描中缀表达式单词列表
如果单词是操作数,则直接添加到后缀表达式列表的
末尾
如果单词是左括号“(”,则压入opstack栈顶
如果单词是右括号“)”,则反复弹出opstack栈顶操作符,加入到输出列表末尾,直到碰到左括号
如果单词是操作符“*/±”,则压入opstack栈顶
• 但在压入之前,要比较其与栈顶操作符的优先级
• 如果栈顶的高于或等于它,就要反复弹出栈顶操作符,加入到输出列表末尾
• 直到栈顶的操作符优先级低于它
❖中缀表达式单词列表扫描结束后,把opstack栈中的所有剩余操作符依次弹出,添加到输出列表末尾
❖把输出列表再用join方法合并成后缀表达式字符串,算法结束

总结:
在从左到右扫描逐个字符扫描中缀表达式的过程中,采用一个栈来暂存未处理的操作符
栈顶的操作符就是最近暂存进去的,当遇到一个新的操作符,就需要跟栈顶的操作符比较下优先级,再行处理

from stack import Stack

def infixToPostfix(string):
    opstack = Stack()
    postfixlist = []
    token = string.split(' ')
    prec = {
 '*':3, '/':3, '+':2, '-':2, '(':1
}
    for astr in token:
        if astr in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789':
            postfixlist.append(astr)
        elif astr == '(':
            opstack.push(astr)
        elif astr == ')':
            while opstack.peek() != '(':
                postfixlist.append(opstack.pop())
            else:
                opstack.pop()
        elif astr in '*/+-':
            if not opstack.isEmpty():
            #要考虑栈是否为空,不然索引会出错
                while prec[astr] <= prec[opstack.peek()]:
                    postfixlist.append(opstack.pop())
                else:
                    opstack.push(astr)
            else:
                opstack.push(astr)
    
    while not opstack.isEmpty():
        #要考虑栈不为空,则一直出栈
        postfixlist.append(opstack.pop())
        return ' '.join(postfixlist)

print(infixToPostfix('( ( A - B ) * B )'))

初看算法不能完全理解,就按照流程编程,非常有助于理解

后缀表达式求值

问题描述:
在对后缀表达式从左到右扫描的过程中,
❖由于操作符在操作数的后面,
❖所以要暂存操作数,在碰到操作符的时候
,再将暂存的两个操作数进行实际的计算
仍然是栈的特性:操作符只作用于离它最近的两
个操作数

思路:
我们弹出两个操作数,计算得到结果30
需要注意:
先弹出的是右操作数
后弹出的是左操作数,这个对于-/很重要!
❖为了继续后续的计算,需要把这个中间结
果30压入栈顶
❖继续扫描后面的符号
❖当所有操作符都处理完毕,栈中只留下1
个操作数,就是表达式的值

demo:

from stack import Stack
def postfixeval(fixString):
    #表达式的符号间隔一个空格
    fix_list = fixString.split(' ')
    opstack = Stack()
    
    for string in fix_list:
        if string in '0123456789':
            #要求操作数是个位数,待改进
            opstack.push(string)
        elif string in '*/+-':
            s = opstack.pop()
            e = eval(opstack.pop() + string + s)
            #要保持数据项为string类型
            opstack.push(str(e))

    return opstack.pop
print('3 2 2 * -')

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