python数据结构与算法-05_栈

news/2024/11/9 9:54:22/

栈这个词实际上在计算机科学里使用很多,除了数据结构外,还有内存里的栈区 (和堆对应),熟悉 C 系语言的话应该不会陌生。
上一章我们讲到了先进先出 queue,其实用 python 的内置类型 collections.deque 或者我们自己实现的 LinkedList 来实现它都很简单。
本章我们讲讲 后进先出的栈。

生活中的数据结构:

  • 栈。好比在桶里头放盘子,先放的盘子放在了底下,后来的盘子放在上边。你要拿的时候,也是先拿最上边的。

栈其实也很简单,因为基础操作就俩,一个 push 和一个 pop,咦,咋和队列一样的?
确实方法名字一样,但是得到的结果可是不同的。

栈 ADT

上一章我介绍了我们怎样选取恰到的数据结构来实现新的 ADT?你能想到这里我们应该使用之前提到的哪个数据结构来实现吗?
你的大脑可能开始高(gui)速(su)旋转了,上几章学过的 array, list, deque, LinkedList, CircularDoubleLinkedList, queue
等在大脑里呼啸而过,这个时候可能已经一脸愁容了,到底该选啥?

还用问嘛,当然是时间复杂度最小的啦,大部分情况下空间都是够用的。
其实你会发现栈比队列还简单,因为它只在顶上操作(想象装着盘子的桶),如果有一种数据结构能方便在尾部增减元素不就满足需求了吗。
这个时候如果你忘记了,可以翻翻前几章,看看哪个数据结构符合要求。

想一下,似乎 CircularDoubleLinkedList 循环双端队列是满足的,因为增删最后一个元素都是 O(1)。
不过看了下示例代码,似乎没有 pop() 方法,对,因为我已经把实现 deque 作为思考题了。😂
如果之前你没写出来也没关系,这里我们会再实现它。

视频里我们将借助 CircularDoubleLinkedList 实现 双端队列 Deque ,并且用 Deque 实现 Stack。

Stack over flow 什么鬼?

嗯,stackoverflow 不是一个程序员问答网站吗?没错。
函数的临时变量是存储在栈区的,如果你不幸写了一个没有出口的递归函数,就会这个错。不信你试试:

def infinite_fib(n):return infinite_fib(n-1) + infinite_fib(n-2)
infinite_fib(10)

一大段输出之后就会出现异常: RecursionError: maximum recursion depth exceeded。
后边会讲到递归,递归是初学者比较难理解的概念,在树的遍历等地方还会看到它。

源码

# -*- coding: utf-8 -*-# NOTE: 这里拷贝的 double_link_list.py 里的代码from collections import dequeclass Node(object):def __init__(self, value=None, prev=None, next=None):self.value, self.prev, self.next = value, prev, nextclass CircularDoubleLinkedList(object):"""循环双端链表 ADT多了个循环其实就是把 root 的 prev 指向 tail 节点,串起来"""def __init__(self, maxsize=None):self.maxsize = maxsizenode = Node()node.next, node.prev = node, nodeself.root = nodeself.length = 0def __len__(self):return self.lengthdef headnode(self):return self.root.nextdef tailnode(self):return self.root.prevdef append(self, value):    # O(1), 你发现一般不用 for 循环的就是 O(1),有限个步骤if self.maxsize is not None and len(self) >= self.maxsize:raise Exception('LinkedList is Full')node = Node(value=value)tailnode = self.tailnode() or self.roottailnode.next = nodenode.prev = tailnodenode.next = self.rootself.root.prev = nodeself.length += 1def appendleft(self, value):if self.maxsize is not None and len(self) >= self.maxsize:raise Exception('LinkedList is Full')node = Node(value=value)if self.root.next is self.root:   # emptynode.next = self.rootnode.prev = self.rootself.root.next = nodeself.root.prev = nodeelse:node.prev = self.rootheadnode = self.root.nextnode.next = headnodeheadnode.prev = nodeself.root.next = nodeself.length += 1def remove(self, node):      # O(1),传入node 而不是 value 我们就能实现 O(1) 删除"""remove:param node  # 在 lru_cache 里实际上根据key 保存了整个node:"""if node is self.root:returnelse:    #node.prev.next = node.nextnode.next.prev = node.prevself.length -= 1return nodedef iter_node(self):if self.root.next is self.root:returncurnode = self.root.nextwhile curnode.next is not self.root:yield curnodecurnode = curnode.nextyield curnodedef __iter__(self):for node in self.iter_node():yield node.valuedef iter_node_reverse(self):"""相比单链表独有的反序遍历"""if self.root.prev is self.root:returncurnode = self.root.prevwhile curnode.prev is not self.root:yield curnodecurnode = curnode.prevyield curnode############################################################
# 分割线,下边是本章 内容实现
############################################################class Deque(CircularDoubleLinkedList):   # 注意这里我们用到了继承,嗯,貌似我说过不会用啥 OOP 特性的,抱歉def pop(self):"""删除尾节点"""if len(self) == 0:raise Exception('empty')tailnode = self.tailnode()value = tailnode.valueself.remove(tailnode)return valuedef popleft(self):if len(self) == 0:raise Exception('empty')headnode = self.headnode()value = headnode.valueself.remove(headnode)return valuedef test_deque():dq = Deque()dq.append(1)dq.append(2)assert list(dq) == [1, 2]dq.appendleft(0)assert list(dq) == [0, 1, 2]dq.pop()assert list(dq) == [0, 1]dq.popleft()assert list(dq) == [1]dq.pop()assert len(dq) == 0class Stack(object):def __init__(self):self.deque = Deque()   # 你可以很容易替换为 python 内置的 collections.dequedef push(self, value):self.deque.append(value)def pop(self):return self.deque.pop()class Stack2(object):def __init__(self):self._deque = deque()def push(self, value):return self._deque.append(value)def pop(self):return self._deque.pop()def empty(self):return len(self._deque) == 0def test_stack():s = Stack()s.push(0)s.push(1)s.push(2)assert s.pop() == 2assert s.pop() == 1assert s.pop() == 0import pytest    # pip install pytestwith pytest.raises(Exception) as excinfo:   # 我们来测试是否真的抛出了异常s.pop()assert 'empty' in str(excinfo.value)if __name__ == '__main__':test_stack()

数据结构头脑风暴法

当我们不知道使用什么数据结构来解决问题的时候,《程序员面试金典》这本书的第六章提到了一种方式叫做『数据结构头脑风暴法』。
这种笨方法就是快速过一遍数据结构的列表,然后逐一尝试各种数据结构看看哪个最适合。

在你实现一个更高级的数据结构的时候,如果脑子没有思路,不妨尝试下这个方法,迅速过一遍你所知道的数据结构,看看哪种最适合。(从每个操作的时间复杂度和空间复杂度分析寻找最优解)

思考题

  • 上一章我们用数组实现了队列,其实也能用数组来实现栈,你能自己用数组来实现一个栈的 ADT 吗?
  • 实际上借助 python 内置的 list/collections.deque 结构就很容易实现一个栈,请你尝试实现,本章我们全部使用自己编写的数据结构而没用到 python 内置的数据结构。
  • 这里我们自己实现了 Deque,你能用 python 内置的 collections.deque 实现栈吗?有轮子能直接用的话看起来就简单多了,这里我们为了学习数据结构的实现就避免了直接使用内置结构
  • 哪些经典算法里使用到了栈呢?

Leetcode 练习

https://leetcode.com/problems/implement-queue-using-stacks/


http://www.ppmy.cn/news/1234409.html

相关文章

详解自动化之单元测试工具Junit

目录 1.注解 1.1 Test 1.2 BeforeEach 1.3 BeforeAll 1.4 AfterEach 1.5 AfterAll 2. 用例的执行顺序 通过 order() 注解来排序 3. 参数化 3.1 单参数 3.2 多参数 3.3 多参数(从第三方csv文件读取数据源) 3.4 动态参数ParameterizedTest MethodSource() 4. 测试…

国外聊天IM — Sendbird

接⼝⽂档: https://sendbird.com/docs 好久没写文章了 我在官网找到的pom, 下载不下来,git下载下来,打进项目里不能用,就只能用简单的http了 直接上代码,只是简单的调通代码,根据你自己业务改:…

JVM 监控命令详解

文章目录 JDK 中与常用命令行工具jpsjstatjinfojmap导出 dump 文件查看堆内存信息 jstack JVM 可视化分析工具 JDK 中与常用命令行工具 jps 查看当前服务器正在执行的 Java 进程 $> jps 7584 Application 16433 AdminApplication 14209 Jps 5813 Bootstrap 5575 TestApplic…

nodejs微信小程序+python+PHP-储能电站运营管理系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…

【尚硅谷】第06章:随堂复习与企业真题(面向对象-基础)

第06章:随堂复习与企业真题(面向对象-基础) 一、随堂复习 1. (了解)面向过程 vs 面向对象 不管是面向过程、面向对象,都是程序设计的思路。面向过程:以函数为基本单位,适合解决简单…

ElasticSearch之Health API

查看当前集群全部健康指标的信息,执行如下命令: curl -X GET "https://localhost:9200/_health_report?pretty" --cacert $ES_HOME/config/certs/http_ca.crt -u "elastic:ohCxPHQBEs5*lo7F9"执行结果输出如下: {&quo…

(C)一些题2

1.在 C 语言中&#xff08;以 16位 PC 机为例&#xff09;,5种基本数据类型的存储空间长度的顺序为&#xff08;&#xff09; A . char < int < long int <float < double B . char int < long int<float <double C . char < int < long int …

设计模式篇---外观模式

文章目录 概念结构实例总结 概念 外观模式&#xff1a;为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用。 外观模式引入了一个新的外观类&#xff0c;它为多个业务类的调用提供了一个统一的入口。主要优点…