9.栈(stack)

栈和队列是两个很基础的数据结构

一般栈有两个概念:

    (1)栈区(内存中)

    (2)LIFO(Last In First Out)


栈最好的理解方式,可以理解为一个桶,往桶里放盘子,拿出来的时候只能从顶上开始拿。


           data 

ADT {

          method { push

                          pop


上一节实现了先进先出的循环双端队列(Deque),支持双向的push/pop操作,只要实现了Deque就很容易实现栈。

其实用 python 的内置类型 collections.deque 来实现它也很简单。


用Deque实现stack


先继承之前写的 CircualDoubleLinkedList() 类,通过继承的方式实现Deuqe,如下:

class Deque(CircualDoubleLinkedList):
    def pop(self):
        if len(self) == 0:                      #如果队列为空
            raise Exception('empty')
        tailnode = self.tailnode()              #定义尾节点
        value = tailnode.value                  #尾节点的值
        self.remove(tailnode)                   #删除尾节点,O(1)
        return value                            #返回删除节点的value
    
    def popleft(self):                          #另外一种从左侧删除的方法
        if len(self) == 0:
            raise Exception("empty")
        headnode = self.headnode()
        value = headnode.value
        self.remove(headnode)
        return value
    
    
class Stack():
    def __init__(self):
        self.deque = Deque()                    #使用内置库的collections.deque方法更容易实现
        
    def push(self, value):
        return self.deque.append(value)
    
    def pop(self, value):
        return self.deque.pop(value)

    def __len__(self):
        return len(self.deque)
    
    def is_empty(self):
        return len(self) == 0
    
    

#单元测试
def test_stack():
    s = stack()
    for i in range(3):
        s.push(i)
    assert len(s) == 3
    assert s.pop() == 2
    assert s.pop() == 1
    assert s.pop() == 0
    assert s.is_empty()
    
    import pytest
    with pytest.raise(Exception) as excinfo:
        s.pop()
    assert "empty" in str(excinfo.value)


栈溢出(Stack over flow)

栈有两个主要的使用含义:

    1.数据结构上所说的后进先出的结构

    2.内存结构的栈区

    

当写递归函数的时候,每一层的递归函数都会用栈区来存储每一层函数它的变量值,如果写了一个没有出口的递归函数,就会导致栈溢出。

演示栈溢出:

#栈溢出
def infinite_fib(n):
    return infinite_fib(n-1) + infinite_fib(n-2)

#执行这个函数后,会返回异常
if __name__ == "__main__":
    infinite_fib(10)

出现栈溢出是因为内存分配给栈区的大小是固定的,当写了一个无穷递归的函数,栈空间很快就会被用完,就会抛出builtins.RecursionError: maximum recursion depth exceeded的栈异常。

所以写递归一定要注意必须要有递归出口,这也是写递归函数的坑。


最后,建议看看内置库collections.deque的实现,有轮子直接拿来用,就简单多了。

猜你喜欢

转载自blog.51cto.com/286577399/2161914