栈 | 进退有度

来源:互联网 发布:ati mac驱动 编辑:程序博客网 时间:2024/06/04 18:39

栈,能独立作为一个数据结构,在于它有自己的特点–由此可见,有特点才有可能自立门派。

栈的特点,是操作都在同一个地方进行,这个地方叫栈顶。

入栈、出栈,都在栈顶进行,最先被处理的是最后进栈的元素,“来得早不如来得巧”,这可不是排队。

注意,数据结构的栈,跟c语言内存管理中的“栈”不是一个概念。

图片来自网络

栈的封装实现,哪一门语言可以做到。

为了简化语言的细节与难度,这里选择python,只因为它的易用性。而简化语言的难度,是为了把精力放在算法或数据结构的设计上,而不是纠结于“内存怎么释放”、“怎么自动检测类型”之类的节外生枝的问题上。

专注很重要!

(一)栈的实现

pthon实现栈,很简单,用list(列表)即可。

入栈:s.append('hello stack')出栈:s.pop()栈是否为空:not s偷看一下栈顶:s[-1]栈的长度:len(s)

比如:

s=[]s.append(8)len(s)      #1s.pop()     #返回8not s       #True,为空s.append('hello')   s.append(False) s.append(8.9)len(s)      #3s[-1]       #8.9len(s)      #3s.pop()     #8.9len(s)      #2not s       #False

pop或append后,栈顶的位置都在变化。

(二)栈的应用

任何一个数据结构,都是因为有应用的场景才存在,也就是时势造英雄。但基于这个数据结构,是可以创造出新的算法的,英雄也可造时势。

栈也不例外。

下面举例说说它的应用场景。

图片来自网络

(1)判断左右符号是否配对

左符号:([{,右符号:)]},配对的情况如:’([]){()}’,不配对的情况如:’[{]}]’。

用栈来理解就是:

遍历所有字符。遇左符号则入栈;遇右符号则出栈(如果为空则直接返回False),出栈返回的字符如果与右符号不配对则返回False,如果配对则继续下一个字符。所有字符遍历完后,栈非空则返回False,否则返回True。
def sybol_match(str):    L=['(','{','['];    R=[')','}',']'];    s=[]    for c in str:        if c in L:            s.append(c)        else:            if not s:                return False            a=s.pop()            lp = L.index(a)            rp = R.index(c)            if lp!=rp:                return False    return not s

(2)计算后缀表达式

计算一个表达式时,表达式可以以中缀或后缀的方式录入,而后缀表达式由于不用使用到括号而简化处理,所以是一个普遍的选择。

比如:

中缀:12*(2/2)  后缀:12 2 2 / * 中缀:10-(2*3) 后缀:10 2 3 * -中缀:(3-2)*(9/3)+5     后缀:3 2 - 9 3 / * 5 + 

用栈来理解就是:

遍历所有分割项(以空格切分)。遇到数字则入栈;遇到操作符出栈两次(这里简化为都是二元操作,第一次出栈的为参数2,第二次为参数1),并进行运算,再把结果入栈。遍历完所有分割项后,返回栈中内容(只有一个值)。
operators = {        '+' : lambda p1, p2: p1+p2,        '-' : lambda p1, p2: p1-p2,        '*' : lambda p1, p2: p1*p2,        '/' : lambda p1, p2: p1/p2,        }def calc_postfix(str):    expr = str.split()    s = []    for e in expr:        if e.isdigit():            s.append(int(e))        else:            f = operators[e]            p2 = s.pop()            p1 = s.pop()            s.append(f(p1, p2))    return s.pop()

(3)背包问题

有若干个物品,每一个物品都有重量。背包有最大容量限制,求刚好装满背包最大容量的所有解。

比如:

物品名称    重量物品0     1kg物品1     8kg物品2     4kg物品3     3kg物品4     5kg物品5     2kg背包最大可以装10kg,那可能的解是:[0,2,3,5]、[1,5]等。

用栈来理解就是:

尽情地装(按物品顺序,只要能装得下就装),如果剩余容量刚好为0或者之后的各个物品都装不下了,则出栈,即拿掉最后的一个物品k,再继续从k+1个物品开始装。栈为空而且填装的物品的索引已经超出范围,则结束循环。由于,总会一直出栈到一个物品都没有,再从下一个物品开始填装,所以一定会出现栈为空且没有物品可装的情况。
def knapsack(w, ws):    """    w --背包容量    ws --物品重量列表 [1, 3, ..]    """    ret = []    s = []    i = 0    cnt = len(ws)    rest = w    while s or i < cnt:  # 栈为空或者还有得装        while i < cnt and rest > 0:  # 还有得装且还有容量            if rest >= ws[i]:  # 装得下就装                s.append(i)                rest -= ws[i]            i += 1   # 不管当前的是否装得下,都要尝试下一个        if rest == 0:            ret.append(s[:])  # one solution        i = s.pop()        rest += ws[i]        i += 1    return retif __name__ == '__main__':    # print(sybol_match('[{()}]{}[()]()'))    # print(sybol_match('[({}])'))    # print(calc_postfix('12 2 2 / *'))    # print(calc_postfix('3 2 - 9 3 / * 5 +'))    ret = knapsack(10, [1, 8, 4, 3, 5, 2])     print(ret)

(三)总结

工具的价值在于恰当的使用。栈,一个简单的工具,它可进退有度,是居家旅游常备之选。

欢迎关注我们

原创粉丝点击