栈的数据结构
Python的基本数据类型list是实现栈的基础
通过使用列表的方法,可以实现栈的特性:
列表访问实际上就是对数组的访问,python数组访问非常方便,可以直接通过指定下标分片。
因此,我们有两种实现方式:将列表首端作为栈顶或列表尾端作为栈顶。
这两种实现的算法复杂度是不一样的,因为列表的内存地址是连续的,列表对象寻址实际上是找到列表开始的元素所在的内存地址,尾端实现的栈可以直接在列表尾端追加或删除元素,首端实现的栈,需要将所有元素在内存中移动一遍,因此算法复杂度是O(n)
尾端实现栈顶的push/pop复杂度为O(1)
首端实现栈顶的push/pop复杂度为O(n)
'''
python列表实现栈1:列表的尾端作为栈顶
'''
class PythonStack1():
def __init__(self):
self.my_stack = []
def push(self, item):
self.my_stack.append(item)
def pop(self):
if len(self.my_stack) > 0 :
res = self.my_stack[-1]
self.my_stack.pop()
return res
return None
def peek(self):
if len(self.my_stack) == 0:
return 'Head'
return self.my_stack[len(self.my_stack) - 1]
def isempty(self):
return self.my_stack == []
def size(self):
return len(self.my_stack)
stack1 = PythonStack1()
stack1.push('A')
stack1.push('B')
stack1.push('C')
print(stack1)
print(stack1.size())
stack1.pop()
print(stack1.peek())
stack1.pop()
print(stack1.isempty())
stack1.pop()
print(stack1.isempty())
'''
python列表实现栈2:列表的首端作为栈顶
'''
class PythonStack2():
def __init__(self):
self.my_stack = []
def push(self, item):
self.my_stack.insert(0, item)
def pop(self):
self.my_stack = self.my_stack[1:]
def peek(self):
return self.my_stack[0]
def isempty(self):
return self.my_stack == []
def size(self):
return len(self.my_stack)
stack2 = PythonStack2()
stack2.push('A')
stack2.push('B')
stack2.push('C')
print(stack2)
print(stack2.size())
stack2.pop()
print(stack2.peek())
stack2.pop()
print(stack2.isempty())
stack2.pop()
print(stack2.isempty())
栈的应用
算法题目中,栈是解决问题常用的工具之一,其先入后出的特性常常为算法提供数据缓存。
括号匹配
栈的应用1-1:简单括号匹配
题目:从给出的表达式中识别括号是否是匹配的
思路:
1. 扫描表达式,过滤掉所有不是括号的字符
2. 遇到左括号压栈,遇到右括号弹出,最后栈为空说明括号匹配
3. 遇到右括号的时候要考虑栈是否已经为空,为空则直接判断不匹配
优化:
如果括号字符串长度是基数位,直接判断不匹配
stack1 = PythonStack1()
def get_brackets(string):
stack = stack1
balanced = True
for i in string:
if i == "(":
stack.push("_")
else:
if stack.isempty():
balanced = False
break
else:
stack.pop()
if balanced and stack.isempty():
return True
else:
return False
def is_matching_brackets(expr):
brackets = []
for i in expr:
if i == "(" or i == ")":
brackets.append(i)
if len(brackets) % 2 == 0: # 优化:如果括号字符串长度是基数位,直接判断不匹配
return get_brackets(brackets)
return False
string1 = '(1+2)+5)+()+2+(1+2)'
string2 = '()()()()((()))()()()()'
print(is_matching_brackets(string1))
print(is_matching_brackets(string2))
栈的应用1-2:多种括号匹配
题目:从给出的表达式中识别括号是否是匹配的
思路:
1. 扫描表达式,过滤掉所有不是括号的字符
2. 遇到任何一种左括号都做压栈处理
3. 遇到右括号:a) 如果栈为空,直接判断不匹配
b) 如果栈顶的括号类型不一致,直接判断不匹配
stack1 = PythonStack1()
def get_brackets(string):
'''
处理字符串,筛选出所有的括号
:param string: 输入字符串
:return: 只包含括号的字符串
'''
stack = stack1
balanced = True
for i in string:
if i in ['(', '[', '{']:
stack.push(i)
else:
if stack.isempty():
balanced = False
break
else:
if not matches(stack.peek(), i):
balanced = False
break
stack.pop()
if balanced and stack.isempty():
return True
else:
return False
def matches(arg_a, arg_b):
'''
检查括号类型是否匹配
:param arg_a: 左括号列表
:param arg_b: 右括号列表
:return: 左右括号是否是同一种类型
'''
a_list = ['[', '{', '(']
b_list = [']', '}', ')']
return a_list.index(arg_a) == b_list.index(arg_b)
def is_matching_brackets(expr):
'''
主方法
:param expr: 输入的字符串
:return: 判断括号是否匹配
'''
brackets = []
for i in expr:
if i in ['[', '{', '(', ']', '}', ')']:
brackets.append(i)
if len(brackets) % 2 == 0: # 优化:如果括号字符串长度是基数位,直接判断不匹配
return get_brackets(brackets)
return False
string1 = '[](){}}}}]'
string2 = '[](){}'
print(is_matching_brackets(string1))
print(is_matching_brackets(string2))
进制转换
题目:十进制转换为二进制
思路:
1. 计算方式,给出的数字不断对2取余,取到的数是又低到高的顺序,再由高到低得到最终的二进制结果
2. 需要一个栈来存放余数,弹出
3. 遇到右括号:a) 如果栈为空,直接判断不匹配
b) 如果栈顶的括号类型不一致,直接判断不匹配
stack = PythonStack1()
def get_binary_number(number):
while number > 0:
stack.push(number % 2)
number = number // 2
res = ''
while not stack.isempty():
res += str(stack.peek())
stack.pop()
return res
print(get_binary_number(16))
print(get_binary_number(255))
栈的应用2-2:
题目:十进制转换为N进制,N不大于10
思路:
通过闭包实现N进制的定制
stack = PythonStack1()
def transfer(N):
def inner(number):
while number > 0:
stack.push(number % N)
number = number // N
res = ''
while not stack.isempty():
res += str(stack.peek())
stack.pop()
return res
return inner
get_2num_from_10num = transfer(2)
print(get_2num_from_10num(255))
get_8num_from_10num = transfer(8)
print(get_8num_from_10num(255))
表达式转换
表达式表示法:
中缀表达式:A+(BC)
前缀表达式:+ABC
后缀表达式:ABC*+
前缀表达式和后缀表达式中操作数的前后位置不变
操作符的顺序表示了运算的优先级,不需要括号来表示运算优先级
对于计算机来说,人类常用的中缀表达式的顺序是混乱的,其运算顺序并不是从左到右。
在不改变元素位置的情况下,调整运算符的顺序,就能取消括号,直接通过运算符的顺序直接标识计算顺序:
栈的应用3-1:表达式转换
题目:把中缀表达式转换为后缀表达式
思路:
- 用栈缓存运算符:+、-、*、/、(、)
- 用列表缓存后缀表达式
- 扫描中缀表达式:
遇到操作符是‘(’,push
遇到操作符是‘)’,连续pop,直到遇到‘(’,把‘(’pop后停止
遇到操作符是‘+、-、*、/’,需要比较和栈顶操作符的优先级
>>低于或等于栈顶优先级:连续pop
高于栈顶优先级:push
后缀表达式接受操作数和栈弹出的运算符
expr_stack = PythonStack1()
def priority(item):
'''
比较当前操作符与栈顶操作符的优先级
:param input: 当前操作符
:param stacktop: 栈顶操作符
:return: 当前操作符优先级 < 栈顶操作符优先级
'''
level = {'*': 3, '/': 3, '+': 2, '-': 2, '(': 1, ')': 1}
return level.get(item)
def get_post_expr(middle_expr):
'''
主方法
:param middle_expr: 中缀表达式
:return: 返回后缀表达式
'''
post_expr = []
for i in list(middle_expr):
if i == '(':
expr_stack.push(i)
if i == ')':
topitem = expr_stack.pop()
while topitem != '(':
post_expr.append(topitem)
topitem = expr_stack.pop()
if i in ['*', '/', '+', '-']:
# 比较当前的运算符优先级与栈顶的运算符优先级
while not expr_stack.isempty() and priority(expr_stack.peek()) >= priority(i):
post_expr.append(expr_stack.pop())
expr_stack.push(i) # 如果运算符优先级大于栈顶优先级,加入到栈
if i in 'ABCDEFGHIJK' or i in '0123456789':
post_expr.append(i) # 把非运算符加入到表达式列表中
while not expr_stack.isempty():
post_expr.append(expr_stack.pop())
res = ''.join(post_expr)
return res
expr1 = 'A+B*C-(D*E+F)'
print(get_post_expr(expr1))
栈的应用3-2:后缀表达式计算
题目:计算后缀表达式的结果
思路:
- 后缀表达式没有括号,运算符的顺序就是计算的顺序
- 运算都是二目运算,从左网右扫描,非运算符压入栈,遇到非运算符,弹出栈顶前两个非运算符,进行计算,计算结果再压入栈
- 注意栈顶两个元素的弹出顺序,在计算除法和减法时是有顺序要求的
compute_stack = PythonStack1()
def compute_post_expr(post_expr):
for item in list(post_expr):
if item in ['*','/','-','+']:
member2 = compute_stack.pop()
member1 = compute_stack.pop()
res_item = str(member1) + item + str(member2)
compute_stack.push(eval(res_item))
else:
compute_stack.push(item)
return compute_stack
post_expr1 = '942*/33*3+-'
print(compute_post_expr(post_expr1).peek())