- 基础定义
什么是栈:栈是一种有次序的数据项集合。在栈中,数据项的加入和移除都仅仅发生在同一端,这一端叫栈顶(top),没有操作的另一端叫栈底(base)。
特性:后进先出,先进后出
- 栈的基本操作
这里需要知道在python里面是没有栈的,但是我们可以模拟一个栈。
首先我们需要定义的一些栈的基本操作:
Stack() | 创建一个空栈,不包含任何数据项 |
push(item) | 将item加入栈顶,无返回值 |
pop() | 将栈顶数据项移除,并返回,栈被修改 |
peek() | 查看栈顶数据项,返回栈顶数据项但不移除,且不修改栈 |
isEmpty() | 返回栈是否为空 |
size() | 返回栈中有多少个数据项 |
这里,我们可以用list类型实现一个栈的操作:
1 class Stack: 2 ''' 3 自己定义的一个栈 4 ''' 5 def __init__(self): 6 self.items =[] 7 def isEmpty(self): 8 return self.items==[] 9 def push(self,item): 10 return self.items.append(item) 11 def pop(self): 12 return self.items.pop() 13 def peek(self): 14 return self.items[len(self.items)-1] 15 def size(self): 16 return len(self.items) 17 if __name__ == '__main__': 18 s = Stack() 19 s.push('1') 20 s.push('a') 21 print(s.isEmpty()) 22 print(s.peek()) 23 print(s.size())
1 [out]: 2 False 3 a 4 2 5 6 Process finished with exit code 0
- 栈的应用
- 简单括号匹配
任务:对括号左右对应匹配,构造一个括号匹配算法。从左到右扫描括号,最新打开的左括号,应该匹配最先遇到的右括号
流程图:
代码:
1 from Stack import Stack 2 def parChecker(symbolString): 3 s = Stack() 4 balanced = True 5 index = 0 6 while index < len(symbolString) and balanced: 7 symbol = symbolString[index] 8 if symbol == "(": 9 s.push(symbol) 10 else: 11 if s.isEmpty(): 12 balanced = False 13 else: 14 s.pop() 15 index = index + 1 16 if balanced and s.isEmpty(): 17 return True 18 else: 19 return False 20 print(parChecker('((()))))')) 21 print(parChecker('(((())))'))
[OUT]:
1 False 2 True 3 4 Process finished with exit code 0
但是在实际应用中,括号往往比较复杂,比如{}()[]:
代码:
1 from Stack import Stack 2 def parChecker2(symbolString): 3 s = Stack() 4 balanced = True 5 index = 0 6 while index < len(symbolString) and balanced: 7 symbol = symbolString[index] 8 if symbol in "({[": 9 s.push(symbol) 10 else: 11 if s.isEmpty(): 12 balanced = False 13 else: 14 top = s.pop() 15 if not matches(top,symbol): 16 balanced = False 17 index = index + 1 18 if balanced and s.isEmpty(): 19 return True 20 else: 21 return False 22 def matches(open,close): 23 opens = '({[' 24 closers = ')}]' 25 return opens.index(open) == closers.index(close) #匹配open和close在opens和closers中的位置索引是否相等 26 print(parChecker2('{{([][])}()}')) 27 print(parChecker2('[{()}'))
[out] E:ProgramsPythonPython38python.exe "F:/yesheng/DATA/code/data struct/parchecker.py" True False Process finished with exit code 0
参考:https://www.bilibili.com/video/BV1QJ411w7bB?p=17
2. 进制转换
1)十进制转换为二进制:除2取余
1 from Stack import Stack 2 3 def divideBy2(decNumber): 4 ''' 5 次序反转 6 :param decNumber: 十进制 7 :return: 二进制 8 ''' 9 remstack = Stack() 10 11 while decNumber > 0: 12 rem = decNumber % 2 #余数 13 remstack.push(rem) 14 decNumber = decNumber // 2 #整除数 15 16 binString = "" 17 while not remstack.isEmpty(): 18 binString = binString + str(remstack.pop()) 19 return binString 20 print(divideBy2(42))
[out] E:ProgramsPythonPython38python.exe "F:/yesheng/DATA/code/data struct/jinzhi.py" 101010 Process finished with exit code 0
2)十进制转换为任意进制
1 def baseConverter(decNumber,base): 2 ''' 3 十进制转换为十六以下任意进制 4 :param decNumber:十进制 5 :param base:目标N进制 6 :return:转换结果 7 ''' 8 digits = '0123456789ABCDEF' 9 10 remstack = Stack() 11 while decNumber > 0: 12 rem = decNumber % base 13 remstack.push(rem) 14 decNumber = decNumber // base 15 newString = '' 16 while not remstack.isEmpty(): 17 newString = newString + digits[remstack.pop()] 18 return newString 19 print(baseConverter(25,2)) 20 print(baseConverter(25,16))
[OUT]
E:ProgramsPythonPython38python.exe "F:/yesheng/DATA/code/data struct/jinzhi.py" 11001 19 Process finished with exit code 0
3. 表达式转换
对于一些全括号表达式,移动操作符后和得到前缀和后缀表达式。
前缀和后缀表达式,操作符的次序决定运算的次序,离操作符近的先做,远的后做。
看第一个,A+B*C+D,对于前缀表达式,首先与操作符最近的是*BC,所以,先做BC乘法,第二近的是+A,所以紧接着做A+B*C,最后是A+B*C+D,后缀表达式以此类推。
因此,将中缀表达式转换为前缀表达式和后缀表达式的思路是:先将中缀表达式转化为全括号的形式,然后将所有的操作符移动到子表达式所在的左括号(前缀)或者右括号(后缀)处,先替代括号,然后删除所有括号。
这里介绍下通用的中缀表达式转后缀表达式算法:
在从左到右扫描逐个字符扫描中缀表达式的过程中,用一个Stack来暂时保存未处理的操作符,此时,栈顶的操作符就是最近暂存进去的,当遇到一个新的操作符,就需要跟栈顶的操作符比较下优先级,再进行处理。
例如:
( A * B ) + ( C * D )
1 from Stack import Stack 2 3 def infixTOPostfix(infixexpr): 4 prec = {} 5 #记录操作符的优先级 6 prec['*'] = 3 7 prec['/'] = 3 8 prec['+'] = 2 9 prec['-'] = 2 10 prec['('] = 1 11 opStack = Stack() 12 postfixList = [] 13 tokenList = infixexpr.split() #将表达式转换成list 14 15 for token in tokenList: 16 if token in 'ABCDEFGHIJKLIMOPQRSTUVWXYZ' or token in '0123456789': 17 postfixList.append(token) #元素直接保存 18 elif token == '(': 19 opStack.push(token) 20 elif token ==')': 21 topToken = opStack.pop() 22 while topToken != '(': 23 postfixList.append(topToken) 24 topToken = opStack.pop() 25 else: 26 while (not opStack.isEmpty()) and (prec[opStack.peek()] >= prec[token]): 27 postfixList.append(opStack.pop()) 28 opStack.push(token) 29 while not opStack.isEmpty(): 30 postfixList.append(opStack.pop()) 31 return ''.join(postfixList) 32 s = "( A * B ) + ( C * D )" 33 print(infixTOPostfix(s))
[out] AB*CD*+ Process finished with exit code 0
4. 后缀表达式求值
问题:在对后缀表达式从左至右扫描过程中,由于操作符在操作数后面,因此要求后缀表达式形式的值,需要先暂存操作数,然后在碰到操作符的时候,再将暂存的操作数结合操作符进行实际计算。(仍然是栈的特性:操作符只作用于离它最近的两个操作数,最后入栈的最先计算)。
例子:4 5 6 * +
算法流程:先将 4 入栈,再将5入栈,接着将6入栈,然后将5 6 出栈进行乘法操作,将结果入栈,然后再将栈里面栈顶以及栈顶后面的元素做加法得到结果。
代码:
1 from Stack import Stack 2 3 def postfixEval(postfixExpr): 4 operandStack = Stack() 5 tokenList = postfixExpr.split() 6 7 for token in tokenList: 8 if token in '0123456789': 9 operandStack.push(int(token)) 10 else: 11 operand2 = operandStack.pop() 12 operand1 = operandStack.pop() 13 result = doMath(token,operand1,operand2) 14 operandStack.push(result) 15 return operandStack.pop() 16 def doMath(op, op1, op2): 17 if op == '*': 18 return op1 * op2 19 elif op == '/': 20 return op1 / op2 21 elif op == '+': 22 return op1 + op2 23 elif op == '-': 24 return op1 - op2 25 print(postfixEval('4 5 6 * +'))
[OUT] 34 Process finished with exit code 0