数据结构与算法学习_线性结构之栈的Python实现

数据结构和算法是计算机技术的基本功之一,北京大学的课程深入浅出,使用Python作为载体简化了编程难度。今天浏览了17-21,主要介绍了【栈】作为一种重要的基本线性数据结构的特点和作用。栈的特点在于后进先出,即后进栈的元素一定要比之前进的元素先出栈。为实现这一结构,使其复杂度为O(1),使用Python中List类型的List.append()和List.pop()来实现,这两个方法都是Python实现的高效操作,满足我们的需求。

线性结构的特点是有序数据项的集合,每个项都有前驱和后继。栈结构是后进先出的线性结构。

接下来用三个例子表现了栈结构的用法和优点:1、括号匹配 2、多种进制转换 3、不同风格计算指令的转化与计算

特将笔记整理如下,算法实现的要点在注释中给出,存在多种实现方式:

有序数据项的集合,每个数据项都有唯一的前驱和后继

每个项都有唯一的【前驱】和【后继】

左端右端,顶端底端

有的只允许一端添加,有的双端可添加移除,有的中间位置也可

四种基本结构,共同点是数据项之间只存在先后次序关系

栈Stack-抽象数据类型

只在一端加入和移除,后进先出

离栈底越近保存时间越长-反转次序

例如浏览器的后退,word的undo

"""
Stack()
push(item)
pop()
peek()窥视栈顶数据项
isEmpty()
size()
"""

将Stack实现为Python的一个class,选用list来实现

list[0] list[-1]末端设为栈底

class Stack:
def init(self):
self.items=[]

def isEmpty(self):
    return self.items==[]

def push(self,item):
    self.items.append(item)
    # self.items.insert(0,item)-栈顶首端,复杂度o(n)

def pop(self):
    return self.items.pop()

def peek(self):
    return self.items[len(self.items)-1]

def size(self):
    return len(self.items)

s=Stack()
s.push('a')
s.push(3)
print(s.peek())#返回 3

栈的应用

1 括号匹配

最新的左括号应该匹配第一个右括号

def parCheck(str1):#str假设只有(()))
s=Stack()
balanced=True
index=0
while index<len(str1) and balanced:
symbol=str1[index]
if symbol in "([{":
s.push(symbol)#左括号放进栈顶
else:
if s.isEmpty():
balanced=False
else:
if (s.peek()=="[" and symbol=="]") or (s.peek()=="{"and symbol=="}") or (s.peek()=="(" and symbol==")"):
s.pop()#根据右括号匹配程度移除左括号
else:
balanced=False
index+=1
if balanced and s.isEmpty():
return True
else:
return False
print(parCheck("({{[]}})"),parCheck("([))"))

2 十进制转换为二进制

从十到二是不停除以2,得到的余数依次是二进制的2 4 8 16

注意到,二进制读取一个数的顺序是从左到右,所以用栈结构,实现先读后得到的数据

def divideBy2(decNumber):
remstack=Stack()#使用字定义的栈类
while decNumber>0:
rem=decNumber%2#pyhton 取余数操作
remstack.push(rem)
decNumber=decNumber//2#python 取整除法操作
binstring=""
while not remstack.isEmpty():#Ensure not empty 校验输出非空
binstring+= str(remstack.pop())
return binstring

print(divideBy2(64)) #输出 1000000 通过

十六进制 0-9 和 ABCDEF

def baseConvrter(decNumber,base):#十进制转十六以下任意进制
digits="0123456789ABCDEF"#定义字母表,需要更多进制扩增此列表即可
remSta=Stack()
while decNumber>0:
rem=decNumber%base#base为希望转成的进制数,rem为递进位数在字母表中的位置
remSta.push(rem)#栈存储位置列表
decNumber=decNumber//base#改变十进制数
returns=""
while remSta.isEmpty()==False:
returns+=str(digits[remSta.pop()])#查表组成输出
return returns

print(baseConvrter(25,16))
print(baseConvrter(25,2)==divideBy2(25))

中缀表达式, a*b,规定优先级,括号强制优先级

全括号表达式

前缀表达式AB,后缀AB

+ABC 前缀 A+BC 后缀 ABC*+,操作符执行最近位置的操作

3 表达式风格转换

首先转化为全括号表达式问题,只需要将某操作符移到右括号处,再删除对应左右括号,

就变成了后缀表达式。移到左括号处就变成了前缀表达式。

【从左往右扫描,每遇到左括号,其后操作符优先级提升,用栈来保存未处理操作符】

新操作符与栈顶操作符比较优先级

约定:中缀表达式由空格隔开的一系列单词token构成

“A,+,B,*,C”

如果token是ABC操作数,直接添加后缀表达式列表末尾

从左到右依次扫描中缀表达式单词列表,如果是左括号则push到opstack栈顶;

在加入之前比较优先级,如果栈顶操作符优先级更高就弹出操作符到输出列表末尾

右括号则【反复弹出opstack栈顶加入到输出列表末尾,直到碰到左括号】

中缀表达式扫描结束后,把栈中所有剩余操作符依次弹出添加到输出列表末尾,join拼接

AB+CD 转化成 ABCD+的后缀表达式

def infixToPostfix(infixstr):
prec={}
prec["*"]=3
prec["/"]=3
prec["+"]=2
prec["-"]=2
prec["("]=1
opStack=Stack()
postfixlist=[]
tokenList=infixstr.split()
for token in tokenList:
if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789":
postfixlist.append(token)
elif token == '(' :
opStack.push(token)
elif token == ')' :
topToken = opStack.pop()
while topToken != '(' :#输出同级所有操作符
postfixlist.append(topToken)#高优先操作符输出
topToken = opStack.pop()
else:
while (not opStack.isEmpty()) and (prec[opStack.peek()] >= prec[token]):
postfixlist.append(opStack.pop())#高优先级栈顶操作符输出
opStack.push(token)#低优先级操作符入栈

while not opStack.isEmpty():
    postfixlist.append(opStack.pop())#输出剩余操作符
return " ".join(postfixlist)

print(infixToPostfix("A * B + C * D"))
print(infixToPostfix("( A + B ) * C - ( D - E ) * ( F + G )"))

4 后缀表达式求值

需要暂存操作数,这依然是栈的特性,因为操作符计算靠的最近的操作数

先出栈右操作数(后入栈)

中间结果一样压入栈顶,继续处理,直到栈中剩下结果值

def postfixEval(postfixExpr):
operandStack=Stack()#操作数栈
tokenList=postfixExpr.split()
for token in tokenList:
if token in "0123456789":
operandStack.push(token)
elif token=="+":
a1=float(operandStack.pop())
a2=float(operandStack.pop())
operandStack.push(a1+a2)
elif token=="":
a1=float(operandStack.pop())
a2=float(operandStack.pop())
operandStack.push(a1
a2)
elif token=="-":
a1=float(operandStack.pop())
a2=float(operandStack.pop())
operandStack.push(a2-a1)
elif token=="/":
a1=float(operandStack.pop())
a2=float(operandStack.pop())
operandStack.push(a2/a1)
return operandStack.peek()

print(postfixEval("3 5 / 2 +"))

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容