数据结构:栈的基本功能接口
并基于Vector模板类作为父类,产生子类Stack,以及栈的典型应用
from Vector import Vector
class Stack(Vector):
def __init__(self):
super(Stack,self).__init__()
# 实现的栈 class 继承自 向量模板类
'''
从栈中:弹出栈顶元素pop和向栈中插入新元素push的时间复杂度都是常数数量级 O(1)
而向向量中任意位置插入或者删除元素的时间复杂度都是O(n-r)
因为当前秩为r之后的所有元素都要向前或者向后移动
'''
def pop(self):
# 弹出栈顶元素,相当于删除向量中的尾部元素
return self.remove(self.length-1)
def push(self,e):
# 将数值为e的新元素入栈,相当于向向量的尾部插入新元素
self.insert(self.length,e)
def top(self):
# 返回栈顶元素的数值,但并不删除该栈顶元素
return self.item[self.length]
def conversion(num,base):
'''
栈的应用之进制转换
:param stack: 输入空栈,
:param num: 所需要进行转换的十进制数
:param base: 要转换成k进制,假设最大为16进制
:return: 进行进制转换后的输出列表,列表中的每个元素是字符类型
'''
char={'0':'0','1':'1','2':'2','3':'3','4':'4','5':'5','6':'6','7':'7','8':'8','9':'9',
'10':'A','11':'B','12':'C','13':'D','14':'E','15':'F'}
stack=Stack()
output=[]
# 使用除k取余法将十进制的数值转换成k进制
while(num):
stack.push(num%base)
num=num//base
for i in range(stack.length):
output.append(char[str(stack.pop())])
return ''.join(output)
def paren(expression):
'''
对输入的表达式判断其中的多组括号对是否匹配,输入的表达式中除了包含括号之外
可能包含别的字符(字母,数字,下划线)
因此首先要经过先行的时间进行从头到尾的扫描,得到只包含括号的表达式
然后判断其中的括号是匹配或者失配
:param expression:
:return:
'''
parentheses_left=['(','[','{']
parentheses_right=[')',']','}']
suit={}
for i,left in enumerate(parentheses_left):
suit[left]=parentheses_right[i]
all_parent_chr=[]
stack=Stack()
for ch in expression:
if ch in parentheses_left+parentheses_right:
all_parent_chr.append(ch)
count=0
# print(all_parent_chr)
for ch in all_parent_chr:
if ch in parentheses_left:
stack.push(ch)# 如果扫描遇到左括号,则入栈
else:
if stack.length==0:# 如果当前扫描遇到右括号,但是栈为空,说明当前的右括号没有对应的左括号与之匹配,
return False
else:
temp_left=stack.pop()
if suit[temp_left]==ch:
count+=1# 得到与当前扫描到的右括号紧邻并且匹配的左括号
else:
return False
# print(count)
return stack.empty()# 最终只有当栈为空时,才说明表达式中的括号是匹配的,否则说明有多余的左括号
def permutation(A,B):
'''
线性时间的栈混洗甄别算法
验证,stack_test是否是stack_1的一个合法的栈混洗序列
对于栈A而言,栈混洗是指:
对于栈A,中转栈S,只允许两种操作:
(1)将A的栈顶元素弹出,并存放到栈S的栈顶
(2)将S的栈顶元素弹出,并存放到栈B的栈顶
则栈B中的元素排列称为栈A的一个栈混洗,不难看出,对于栈A而言,可能存在多种合法的栈混洗(stack permutation)
对于序列长度为n的栈,其栈混洗的序列个数为 (2n)!/((n+1)!n!)
为了判断栈B中的元素排列是否是栈A的合法栈混洗,直接模拟栈混洗的过程
:param stack_1:
:param stack_test:
:return:
'''
S=Stack()#初始化中转栈S
B=B.item[1:-1]
index=0
while(1):
if S.length==0 or S.top()!=B[index]:#说明S是空栈,
if A.length==0:
break
else:
S.push(A.pop())#将A的栈顶元素push到S中
else:
if S.top()==B[index]:
S.pop()
index+=1
if S.length==0 and A.length==0:
return True
else:
return False
def evaluate(expression):
'''
中缀表达式求值(中缀表达式是指:操作运算符在两个操作数中间的表达式)
在当前输入的表达式序列中寻找一个能够优先计算的子串
并将计算所得的数值重新放置到子串所在的位置上
使得经过这样的转换之后,新表达式的数值与原始表达式的数值完全相同
如果将计算表达式所需要的时间复杂度看作是表达式中运算操作符(+-*/%)的个数
则这样的过程依然可以看作是减而治之(decrease and conquer)
最终将消除掉所有的运算符,从而得到最终的数值计算结果
:return:
'''
operand_stack=Stack()# 初始化操作数栈
operator_stack=Stack()#初始化操作符栈
num_flag=False# 表示上一次遇到的字符是数字还是操作符
num_list=['0','1','2','3','4','5','6','7','8','9']
expression+='$'
priority_dict={'$':-1,'(':0,'+':1,'-':1,'*':2,'/':2,'^':3,'!':4,')':0}
# 优先级字典,其中只有!操作符只需要一个操作数(一元运算符)且优先级最高,其他都是二元运算符
i=0
operator_stack.push('$')
# print('expression',expression)
# expression (0!+1)*2^(3!+4)-(5!-67-(8+9))
while(i<len(expression)):
if expression[i] in num_list:#如果当前扫描到的字符是操作数
if num_flag==False:# 如果上一次扫描到的不是操作数而是操作符
operand_stack.push(int(expression[i]))
else:# 如果上一次扫描到的也是操作数,则将操作数栈中的栈顶元素出栈,*10,之后加上当前的数值,再入栈
before_num=operand_stack.pop()
operand_stack.push(before_num*10+int(expression[i]))
i+=1#扫描光标指向下一个字符
num_flag=True
else:# 如果当前遇到的是操作符
if expression[i]=='$' and operator_stack.top()=='$':
operator_stack.pop()
break
# 相当于左括号和右括号的作用,直接将数值弹出
num_flag=False
if expression[i] == ')' and operator_stack.top()=='(': # 如果当前处理的是右括号
# 则说明当前的一对左右括号中的数值都计算完毕
operator_stack.pop() # 弹出栈顶的左括号,并且扫描光标指向下一个元素
i += 1
elif operator_stack.top()=='(' or expression[i]=='(':
#如果操作符栈的栈顶元素是左括号或者当前处理的字符是左括号,则无条件将当前的操作符入栈
operator_stack.push(expression[i])
i+=1
elif priority_dict[expression[i]]<=priority_dict[operator_stack.top()]:
# 如果当前的运算符的优先级比操作符栈的栈顶元素优先级低,说明栈顶运算符的操作时机到了
# print('before here',operator_stack.item)
operator=operator_stack.pop()
# print('here',operator,operator_stack.item)
if operator=='!':#一元操作符
from functools import reduce
num=operand_stack.pop()
if num==0:
out=1
else:
out = reduce(lambda x, y: x * y, range(1, num+1))
operand_stack.push(out)
else:
right = operand_stack.pop()
left = operand_stack.pop()
if operator=='+':
operand_stack.push(left+right)
elif operator=='-':
operand_stack.push(left-right)
elif operator=='*':
operand_stack.push(left*right)
elif operator=='/':
operand_stack.push(left/right)
elif operator=='^':
operand_stack.push(left**right)
else:
# 如果当前扫描到的操作符的优先级比操作符栈顶优先级高,则将当前操作符入栈
operator_stack.push(expression[i])
i+=1
# print('jfosjdosajod-----',expression[i], operator_stack.item,operand_stack.item)
# print('operator_stack',operator_stack.item)
# print(operand_stack.item,operator_stack.item)
# operator_stack.pop()
assert operator_stack.length==0
assert operand_stack.length==1
return operand_stack.pop()
def Infix_2_Postfix(expression):
'''
对于中缀表达式求值,在之前的计算过程中
界定运算符之间的优先级有两种形式:
(1)通过括号强制指定优先级的高低
(2)认为指定,比如+- < * / < ^ <!
现在引入逆波兰表达式(逆波兰表达式就是后缀表达式),不需要任何运算符优先级的界定
对于逆波兰表达式的计算,通过运算符出现的次序先后界定给运算符的优先级(准确的说,是先进行什么计算,后进行什么计算)
如何求解逆波兰表达式的数值?
光标逐个扫描表达式中的字符
(1)对于操作数,无条件直接入栈
(2)对于操作符,则直接从操作数栈中取出相应个数的数值,执行运算,并将运算后的数值push进入操作数栈
操作过程中仅仅借助一个辅助栈(操作数栈),就可以得到最终的计算结果
如何将中缀表达式转换成逆波兰表达式(实际上就是保证操作数的相对顺序不变,将操作符的位置移动到它合适的时机)
后缀表达式中不包含任何括号
:return:
'''
Postfix=Stack()
operator_stack=Stack()
operator_stack.push('$')
i=0
num_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
priority_dict = {'$': -1, '(': 0, '+': 1, '-': 1, '*': 2, '/': 2, '^': 3, '!': 4, ')': 0}
expression+='$'
num_flag=False
while(i<len(expression)):
if expression[i] in num_list:# 如果当前扫描到的字符是操作数
if num_flag==False:
Postfix.push(int(expression[i]))
else:
last_num=Postfix.pop()
temp=last_num*10+int(expression[i])
Postfix.push(temp)
num_flag=True
i+=1
else:
num_flag=False
# 如果当前扫描到的是操作符
top=operator_stack.top()
if top=='$' and expression[i]=='$':
operator_stack.pop()
break
elif operator_stack.top=='(' or expression[i]=='(':
operator_stack.push(expression[i])
i+=1
elif operator_stack.top=='(' or expression[i]=='(':
operator_stack.pop()
i+=1
elif priority_dict[top]>=priority_dict[expression[i]]:
Postfix.push(operator_stack.pop())
else:
operator_stack.push(expression[i])
i+=1
print(Postfix.item)# (0!+1)*2^(3!+4)-(5!-67-(8+9))
# 得到后缀表达式之后,计算后缀表达式的数值
# 扫描后缀表达式中的每个字符,如果遇到了左括号或者右括号,则需要直接忽略
# 因为后缀表达式的操作符先后计算次序并不是由括号或者操作符本身的优先级所指定的
output_stack=Stack()
for elem in Postfix.item[1:-1]:
if isinstance(elem,int):
output_stack.push(elem)# 操作数直接入栈
else:# 对于操作符
if elem=='!':#如果是一元操作符
temp=output_stack.pop()
f=lambda x:x*f(x-1) if x>1 else 1
output_stack.push(f(temp))
else:
if elem=='(' or elem==')':
continue
operate = {'+': lambda x, y: x + y,
'-': lambda x, y: x - y,
'*': lambda x, y: x * y,
'/': lambda x, y: x / y,
'^': lambda x, y: x ** y}
operate_2=output_stack.pop()
operate_1=output_stack.pop()
output_stack.push(operate[elem](operate_1,operate_2))
# print('here',output_stack.item)
assert output_stack.length==1
return output_stack.pop()
if __name__=='__main__':
stack_1=Stack()
print(stack_1.traverse())
print(stack_1.empty())
stack_1.push(5)
print(stack_1.traverse())
stack_1.push(3)
print(stack_1.traverse())
print('pop',stack_1.pop())
print(stack_1.traverse())
stack_1.push(7)
print(stack_1.traverse())
stack_1.push(3)
print(stack_1.traverse())
print(stack_1.top())
print(stack_1.empty())
stack_1.push(11)
print(stack_1.traverse())
print(stack_1.size())
print(conversion(12345, 8))
test_str_1='(a[i+1][j+1])+a[i+1][j-1])*2'
print(paren(test_str_1))
test_str_2 = '(a[i+1][j+1]+a[i+1][j-1])*2'
print(paren(test_str_2))
A=Stack()
A.push(4)
A.push(3)
A.push(2)
A.push(1)
# print(A.item)
B=Stack()
B.push(3)
B.push(2)
B.push(1)
B.push(4)
print(B.item)
print('here',permutation(A,B))
print(evaluate('(0!+1)*2^(3!+4)-(5!-67-(8+9))'))
print(Infix_2_Postfix(expression='(0!+1)*2^(3!+4)-(5!-67-(8+9))'))