随笔 - 5  文章 - 0  评论 - 0  阅读 - 536

数据结构与算法学习笔记 —— 栈(stack)

栈 Stack

1.简介

# 本片学习笔记为观看北京大学陈斌教授的熟虑结构与算法课程(python版)记录

 

栈是一种有次序的数据项集合,该集合的两端分别为栈底(base)和 栈顶(top)

 

 

 *数据项的添加和移除仅发生在栈顶

 

进出栈原则:后进先出(Last in First Out)

距离栈底过越近的数据项,留在栈中的时间就越长。而最新加入栈的数据项会被最先移除

 

用List实现 ADT Stack:

此处选用List的末端(index = -1)作为栈顶:

 

复制代码
 1 class Stack:
 2     def __init__(self): #构造一个stack对象
 3         self.items = []
 4 
 5     def push(self,item): #将item添加至栈顶,无返回值
 6         self.items.append(item)
 7 
 8     def pop(self): #移除栈顶元素,并返回修改后的栈
 9         return self.items.pop()
10 
11     def peek(self): #返回栈顶元素
12         return self.items[len(self.items)-1]
13 
14     def isEmpty(self): #返回栈是否为空
15         return self.items == []
16 
17     def size(self): #返回栈中数据项的数量
18         return len(self.items)
复制代码

 

注: 选用list左端为栈顶也可,但性能有所不同。栈顶首端为左的情况下,push和pop的复杂度为O(n), 而栈顶尾端右的情况下,push和pop的复杂度为O(1)

 

2.栈的应用

2.1 括号匹配

 

括号的使用必须遵循平衡原则:

1. 每个开口括号,必须对应一个闭口括号

2. 每对括号必须要正确的嵌套

Eg. 

正确的: (()()()()),(((()))),(()((())())) 

错误的: ((((((()),())),(()()(() 

方法: 

从左到右扫面括号串,最新遇到的左括号,应该匹配到最先遇到的右括号。因此,第一个左括号应该匹配到最后一个右括号。这种次序反转的识别模式,刚好符合栈的特性。

所以我们可以从左扫面括号串,若为左边开口括号push到栈顶,若为右括号则先判断栈是否为空。如果空了,那么说明没有左括号与其匹配,则这个括号串是错误的。若栈不为空,则将栈顶的左括号pop(与刚刚扫描到的右括号匹配成功)

扫描完成后,再检查一下栈中的左括号有没有全部被移除(匹配)出去。若仍有剩余,则括号串不合法。

具体流程可见下图:

 

 

 

python代码实现:

复制代码
s1 = "(()((())()))"
s2 = "((((((())"

def parChecker(s):
    stack = Stack()
    for item in s:
        if item == '(': stack.push(item)
        else:
            if stack.isEmpty(): return False
            else: stack.pop()

    return stack.isEmpty()

print(parChecker(s1,s2))
复制代码

* 如果涉及多种不同的括号(如[] {} ()), 这要留意括号的匹配问题

 

2.2 进制转换

将十进制整数N转换为base进制

复制代码
def convert(num,base):
    stack = Stack()
    output = ''
    while num > 0:
        remainder = num % base
        stack.push(str(remainder))
        num = num // base
    while not stack.isEmpty():
        output = output + stack.pop()
    return output
复制代码

 

2.3 表达式转换求值

对输入的表达式进行求值:例如 A + B*C

step1.

首先将表达式转换为后缀表达式,

即 首先将中缀表达式转换为全括号形式。然后将所有的操作符移动到子表达式所在的右括号处,替代之,再删除所有括号

中缀转后缀的流程为:

如果扫描对象token是操作数,则直接添加到后缀表达式列表的末尾。

如果token是左括号,则押入opstack栈顶

如果是右括号,则反复pop出栈顶并添加到后缀表达式列表的末尾

如果操作符,则push到栈顶:

  - 但是在push之前, 要比较其与栈顶操作符的优先级

  - 如果栈顶的高于或等于它,就要反复弹出栈顶操作符,直到栈顶操作符优先级低于它

复制代码
def infixtopostfix(a):
    priority = {
        '*':3,
        '/':3,
        '+':2,
        '-':2,
        '(':1
    }

    opstack = Stack()
    output = []
    a = a.split()
    for token in a:
        if token.isdigit():
            output.append(token)
        elif token == '(':
            opstack.push(token)
        elif token == ')':
            topToken = opstack.pop()
            while topToken != '(':
                output.append(topToken)
                topToken = opstack.pop()
        else:
            while (not opstack.isEmpty()) and priority[token] <= priority[opstack.peek()]:
                output.append(opstack.pop())
            opstack.push(token)
    while not opstack.isEmpty():
        output.append(opstack.pop())
    return output
复制代码

 

求值过程:

从左到右扫描token列表

如果扫描为一个操作数,则将其转化为整数并且push到operandstack栈中;

如果扫描为一个操作符,则pop出栈顶两个数(第一个为有操作数,第二个为做操作数,顺序很重要)。

计算出结果并且push回栈中:

 

复制代码
def postfixeval(l):
    operands = Stack()
    for token in l:
        if token.isdigit():
            operands.push(int(token))
        else:
            operand_r = operands.pop()
            operand_l = operands.pop()
            if token == '-': operands.push(operand_l - operand_r)
            elif token == '+': operands.push(operand_l + operand_r)
            elif token == '*': operands.push(operand_l * operand_r)
            else: operands.push(operand_l/operand_r)
    return operands.pop()
复制代码

 

 

 

 

posted on   无甲的乔  阅读(108)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示