python栈应用十进制转二进制用最少的时间复杂度_数据结构之栈的介绍以及一些简单应用(上)...

线性结构之栈

在说栈的前提下,我们先来说说什么是线性结构,线性结构是一种有序数据项的集合,其中每个数据项都有唯一的前驱和后继,除了第一个没有前驱,最后一个没有后继,新的数据项加入到数据集中时,只会加入到原有某个数据项之前或者之后,具有这种性质的数据集,就成为线性结构。

线性结构总有两端,在不同的情况下,两端的称呼也不同,有时候称为"左"、“右”端,有时候称为“前”、“后”端、有时候称为为“顶”、"底"端。

两端的称呼并不是关键,不同线性结构的关键区别在于数据项的增减方式。有的结构只允许数据从一端添加,而由的结构则允许数据项从两端移除。

栈Stack,队列Queue,双端队列Deque和列表List,这些数据集的共同点在于,数据项之间只存在先后的次序关系,都是线性结构。

栈Stack

什么是栈

一种有次序的数据项集合,在栈中,数据项的加入和移除都仅仅发生在同一端,这一端叫栈“顶top”,另一端叫栈“底base”。

日常生活中有很多栈的应用,比如盘子,书堆等等。

距离栈底最近的数据项,留在栈中的时间就越长,而最新加入栈的数据项会被最先移除。

这种次序通常称为“后进先出“,这是一种基于数据项保存时间的次序,时间越短的离栈顶越近,而时间越长的离栈底越近。

栈的特性:反转次序

我们观察一个由混合的Python原生数据对象形成的栈,如下图所示,进栈和出栈的次序正好相反。

5902f3f72ed69c5aea7deabc23fc287e.png

这种访问次序反转的特性,我们在某些计算机操作上碰到过,浏览器的“后退”按钮,最先后退的是最近访问的网页。

抽象数据类型Stack

抽象数据类型“栈”是一个有次序的数据集,每个数据项仅从“栈顶”一端加入到数据集中,从数据集中移除,栈具有后进先出的特性。

8d577fd0018e816197bd4bb8d9f5316e.png

抽象数据类型“栈”定义为如下的操作:

Stack():创建一个空栈,不包含任何数据项

push(item):将item加入栈顶,无返回值

pop():将栈顶数据项移除,并返回,栈被修改

peek():"窥视"栈顶数据项,返回栈顶的数据项但不移除,栈不被修改

isEmpty():返回栈是否为空栈

size():返回栈中有多少个数据项

操作样例

Stack Operation

Stack Contents

Return Value

s=Stack()

[]

Stack object

s.isEmpty()

[]

True

s.push(4)

[4]

s.push("dog")

[4,"dog"]

s.peek()

[4,"dog"]

"dog"

s.push(True)

[4,"dog",True]

s.size()

[4,"dog",True]

3

s.isEmpty()

[4,"dog",True]

False

s.push(8.4)

[4,"dog",True,8.4]

s.pop

[4,"dog",True]

8.4

s.pop

[4,"dog"]

True

s.size()

[4,"dog"]

2

用Python实现ADT Stack

在清楚地定义了抽象数据类型Stack之后,我们看看如何用Python来实现它,Python的面向对象机制,可以用来实现用户自定义类型。

将Stack实现为Python的一个Class

将Stack的操作实现为Class的方法

由于Stack是一个数据集,所以可以采用Python的原生数据集来实现,我们选用最常用的数据集List来实现

一个细节:Stack的两端对应List的设置。可以将List的任意一端(index=0或-1)设置为栈顶,我们选用List的末端(index=-1)作为栈顶,这样栈的操作就可以通过list的append和pop来实现。

class Stack:

def __init__(self):

self.items = []

def isEmpty(self):

return self.items == []

def push(self, item):

return self.items.append(item)

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()

print(s.isEmpty()) # True

s.push(4)

s.push("dog")

print(s.peek()) # dog

s.push(True)

print(s.size()) # 3

print(s.isEmpty()) # False

s.push(8.4)

print(s.pop()) # 8.4

print(s.pop()) # True

print(s.size()) # 2

ADT Stack的另一种实现

如果我们把List的另一端(首端index=0)作为Stack的栈顶,同样也可以实现Stack。

不同的实现方案保持了ADT接口的稳定性,但性能有所不同,栈顶首端的版本其push/pop的复杂度为O(n),而栈顶尾端的实现,其push/pop的复杂度为O(1)。

class Stack:

def __init__(self):

self.items = []

def isEmpty(self):

return self.items == []

def push(self, item):

return self.items.insert(0, item)

def pop(self):

return self.items.pop(0)

def peek(self):

return self.items[len(self.items)-1]

def size(self):

return len(self.items)

栈的应用

简单括号匹配

我们都写过这样的表达式:(5+6)*(7+8)/ (4+3)

当然,括号的使用必须遵循“平衡”规则,首先,每个开括号要恰好对应一个闭括号;其次,每对开括号要正确的嵌套。对括号是否正确匹配的识别,是很多语言编译器的基础语法。

下面我们看看如何构造括号匹配识别算法:

从左到右扫描括号串,最新打开的左括号,应该匹配最先遇到的右括号,第一个左括号(最早打开),就应该匹配一个右括号(最后遇到),次序反转的识别,正好符合栈的特性。

def parChecker(symbolString):

s = Stack()

balanced = True

index = 0

while index < len(symbolString) and balanced:

symbol = symbolString[index]

if symbol == "(":

s.push(symbol)

else:

s.pop()

index += 1

if balanced and s.isEmpty():

return True

else:

return False

更多种括号匹配

在实际的应用中,我们会碰到更多的括号,如Python中列表所用的方括号“[]”,字典所用的花括号“{}”,元祖和表达式所用的圆括号"()"。

这些不同的括号可能会混合在一起使用,因此就要注意各自的开闭匹配情况。

那么我们可以把上面的代码改成通用匹配不?

我们依旧是碰到各种左括号仍然入栈,碰到各种右括号的时候需要判断栈顶的左括号是否跟它是同一类?

def parChecker(symbolString):

s = Stack()

balanced = True

index = 0

while index < len(symbolString) and balanced:

symbol = symbolString[index]

if symbol in "([{":

s.push(symbol)

else:

top = s.pop()

if not matches(top, symbol):

balanced = False

index += 1

if balanced and s.isEmpty():

return True

else:

return False

def matches(open, close):

opens = "([{"

closers = ")]}"

return opens.index(open) == closers.index(close)

十进制转二进制

我们经常需要将整数在二进制和十进制之间转换,十进制转换为二进制,采用的是“除以2求余数”的算法,将整数不断除以2,每次得到的余数就是由低到高的二进制位。

4b6dc75ed87bd6309e5d7432df50cb74.png

“除以2”的过程,得到的余数是从低到高的次序,而输出则是从高到低,所以需要一个栈来反转次序。

aa958cd9f75cf1f1e1e27b6b44a63c3e.png

def divideBy2(number):

s = Stack()

while number > 0:

rem = number % 2 # 求余

s.push(rem)

number //= 2 # 整除2

binString = ""

while not s.isEmpty():

binString += str(s.pop())

return binString

扩展到更多进制转换

十进制转换为二进制的算法,很容易可以扩展到转换为任意N进制,只需要将"除以2求余数"算法改为“除以N求余数”算法就可以了。

def baseConverter(number,base):

digits = "0123456789ABCDEF"

s = Stack()

while number > 0:

rem = number % base

s.push(rem)

number //= base

newString = ""

while not s.isEmpty():

newString += digits[s.pop()]

return newString

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值