这一节主要讲述栈和队列,这两种数据结构使用python的实现,并且讲述了这两种数据结构的常见问题和方法。
栈的实现
class Stack:
def __init__(self):
self.item=[]
def isEmpty(self):
return self.item==[]
def push(self,i):
self.item.append(i)
def pop(self):
return self.item.pop()
def peek(self):
return self.item[len(self.item)-1]
def size(self):
return len(self.item)
使用python里面的列表来实现一个栈的数据结构,实现的主要功能包括:
1、长度
2、是否为空
3、添加元素
4、推出栈尾元素
5、peek。显示队尾元素,不推出。
主要功能是 添加+推出 元素,直接使用python列表的append函数和pop函数即可,较为简单。
注: 这里有一点编程时容易错的,size在类中是一个函数,所以每次调用时需要使用s.size(),很多时候容易犯错写成s.size,就比较容易错。
队列的实现
class Queue:
def __init__(self):
self.item=[]
def isEmpty(self):
return self.item==[]
def enqueue(self,x):
self.item.insert(0,x)
def dequeue(self):
return self.item.pop()
def size(self):
return len(self.item)
同样使用python的列表来实现队列。主要功能还是针对 添加元素和 推出元素:因为队列先进先出的特点,所以没有栈结构那么直接,推出元素还是使用pop,那么添加元素就需要从队首添加,于是可以使用insert函数:
def enqueue(self,x):
self.item.insert(0,x)
其余的功能函数实现方法就和栈差不多了。
经典应用场景
下面介绍几个栈和队列数据结构解决问题的经典案例
问题一:括号匹配
描述:假设在一段字符串中,有如下几种括号<> () [] {},如果想要符合语义,每一个左括号需要在合适的位置对应一个右括号。给定一段充满括号的字符串,判断是否其中的括号是否是平衡的。
实现:
def isBrakectsMatch(givenstr):
s=Stack()
LeftBrakects=['<','(','[','{']
RightBrakects=['>',')',']','}']
for i in givenstr:
if i in LeftBrakects:
s.push(i)
elif i in RightBrakects:
if s.size()==0:
return False
else:
if LeftBrakects.index(s.pop())!=RightBrakects.index(i):
return False
else:
pass
else:
pass
if s.size() == 0:
return True
else:
return False
核心思想:扫描这段字符串,如果碰到左括号,就压入栈中;如果碰到右括号,就用它和栈中推出的末端元素比对。最后扫描完字符串,栈中无元素,则匹配。若不是,或者中间匹配时无法对得上,则不匹配。
细节点:
(1)碰到右括号比对时,先把左右括号放在两个闲置的列表中,然后用列表的index进行比对;
(2)栈数据结构的长度,使用s.size(),括号不能少,否则程序出错。
问题二:表达式转换
背景知识:中缀表达式就是运算符在中间的表达式,数学中都是这种。运算中有所谓的运算优先级,比如是乘除优先于加减。为了方便计算机能够识别优先级,引入了全括号表达式:以最里面的括号为最优先。
前缀表达式和后缀表达式,就是把表达符放在前面或者后面。前后缀表达式的一个优点是不需要注意优先级,且不需要括号,就可以直接按正确的顺序进行运算。前后缀表达式和全括号表达式的转换规则如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yop4a5El-1598376573912)(C:\Users\wangzihao\AppData\Roaming\Typora\typora-user-images\image-20200823041843174.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-75G7bytw-1598376573915)(C:\Users\wangzihao\AppData\Roaming\Typora\typora-user-images\image-20200823041940801.png)]
描述:将正常的中缀表达式转换成相应的前缀表达式和后缀表达式。以及如何转换回来?(通用算法)
def Middle2After(expressionStr):
s=Stack()
strList = expressionStr.split(' ')
outList=[]
markvalue={'+':1,'-':1,'*':2,'/':2,'(':0}
for i in strList:
if i == '(':
s.push(i)
elif i ==')':
temp=s.pop()
while temp!='(':
outList.append(temp)
temp=s.pop()
elif i in markvalue:
while s.size()!=0 and markvalue[s.peek()]>markvalue[i]:
outList.append(s.pop())
else:
s.push(i)
else:
outList.append(i)
while s.size()!=0:
outList.append(s.pop())
return outList
实现的核心逻辑如下:
1、先划分出运算符的自然权重,乘除较高,加减较低,后面做比较;
markvalue={'+':1,'-':1,'*':2,'/':2,'(':0}
2、遇到运算符,先压进栈。只有当栈顶的运算符确实比下面那个高的时候,才能够输出这个栈顶运算符;
3、括号权限较重,所以遇到左括号时先压进栈,遇到右括号时,把栈中一直到左括号的运算符全部输出来。
问题三:热土豆问题
描述:有M个土豆围成一圈,从1编号到M,然后从1开始,每n个土豆就拿一个去加热吃掉,问最后留下来的那个土豆是一开始在哪个编号上的?
实现代码:
def HotTomato(m,n):
q=Queue()
for i in range(1,m+1):
q.enqueue(i)
num=1
while q.size()!=1:
temp=q.dequeue()
if num%n!=0:
q.enqueue(temp)
num=num+1
return q.dequeue()
细节讲解:
1、核心思想一目了然,全部放到队列里,然后每次推一个再进一个,除非这个是n的倍数(即拿去加热);
2、函数暂时没有考虑M,N数值非法的情况,默认是OK的