Python堆栈详细介绍

news2024/11/27 14:38:16

                                                          a52241369f081fc58e4c30b0b4d3782f.png


概要

虽然一些数据结构是通用的并且可以在广泛的应用中使用,但其他数据结构是专门化的并且被设计用于处理特定问题。堆栈就是这样一种专门的结构,以其简单性和非凡的实用性而闻名。

那么,什么是栈呢?从本质上讲,堆栈是一种遵循LIFO(后进先出)原则的线性数据结构。

多年来,堆栈已经在许多领域找到了应用,从您最喜欢的编程语言中的内存管理到 Web 浏览器中的后退按钮功能。这种内在的简单性与其广泛的适用性相结合,使该堆栈成为开发人员工具库中不可或缺的工具。

堆栈数据结构的基本概念

从本质上讲,堆栈看似简单,但它所具有的细微差别使其在计算领域具有多种应用。在深入研究其实现和实际用途之前,让我们确保对堆栈的核心概念有一个透彻的理解。

LIFO(后进先出)原则

LIFO是堆栈背后的指导原则。这意味着最后进入堆栈的项目是第一个离开的项目。这一特性将堆栈与其他线性数据结构(例如队列)区分开来。

注意:另一个帮助您理解堆栈如何工作的概念的有用示例是想象人们进出电梯-最后进入电梯的人是第一个出去的!

基本操作

每个数据结构都是由它支持的操作定义的。对于堆栈来说,这些操作很简单但至关重要:

  • Push - 将一个元素添加到堆栈顶部。如果堆栈已满,则此操作可能会导致堆栈溢出。

dc7d099867f3409483390a34ab3a1405.png

  • Pop - 删除并返回堆栈最顶层的元素。如果堆栈为空,尝试弹出可能会导致堆栈下溢。

0db3bf21927e41b9a1226ccf86da81fd.png

  • Peek(或 Top) - 观察最上面的元素而不删除它。当您想要检查当前顶部元素而不更改堆栈状态时,此操作非常有用。

如何在 Python 中从头开始实现堆栈

掌握了堆栈背后的基本原理后,是时候卷起袖子深入研究事物的实际方面了。实现堆栈虽然简单,但可以通过多种方式实现。在本节中,我们将探讨实现堆栈的两种主要方法 - 使用数组和链表。

使用数组实现堆栈

数组是连续的内存位置,提供了一种直观的方式来表示堆栈。它们允许按索引访问元素的时间复杂度为 O(1),从而确保快速推送、弹出和查看操作。此外,数组可以提高内存效率,因为没有链表中的指针开销。

另一方面,传统数组具有固定大小,这意味着一旦初始化,它们就无法调整大小。如果不进行监控,这可能会导致堆栈溢出。这可以通过动态数组(如 Python 的list)来克服,它可以调整大小,但此操作的成本相当高。

完成所有这些后,让我们开始在 Python 中使用数组实现我们的堆栈类。首先,让我们创建一个类本身,其构造函数将堆栈的大小作为参数:  ​​​

class Stack:
    def __init__(self, size):
        self.size = size
        self.stack = [None] * size
        self.top = -1

我们在类中存储了三个值。是size所需的堆栈大小,是stack用于表示堆栈数据结构的实际数组,是数组中最后一个元素(堆栈顶部)top的索引。stack

从现在开始,我们将为每个基本堆栈操作创建并解释一种方法。这些方法中的每一个都将包含在Stack我们刚刚创建的类中。

我们先从push()方法开始吧。如前所述,入栈操作将一个元素添加到堆栈顶部。首先,我们将检查堆栈是否还有空间供我们要添加的元素使用。如果堆栈已满,我们将引发Stack Overflow异常。否则,我们只需添加元素并相应地调整top和stack: ​​​​​

def push(self, item):
        if self.top == self.size - 1:
            raise Exception("Stack Overflow")
        self.top += 1
        self.stack[self.top] = item

现在,我们可以定义从栈顶移除元素的方法——方法pop()。在尝试删除元素之前,我们需要检查堆栈中是否有任何元素,因为尝试从空堆栈中弹出元素是没有意义的: ​​​​

def pop(self):
        if self.top == -1:
            raise Exception("Stack Underflow")
        item = self.stack[self.top]
        self.top -= 1
        return item

最后,我们可以定义peek()只返回当前堆栈顶部元素的值的方法: ​​​​​​

def peek(self):
    if self.top == -1:
        raise Exception("Stack is empty")
    return self.stack[self.top]

我们现在有一个类,它在 Python 中使用列表实现堆栈的行为。

使用链表实现堆栈

链表是动态数据结构,可以轻松增长和收缩,这对于实现堆栈是有利的。由于链表根据需要分配内存,因此堆栈可以动态增长和减少,而无需显式调整大小。使用链表实现堆栈的另一个好处是入栈和出栈操作只需要简单的指针变化。这样做的缺点是链表中的每个元素都有一个额外的指针,与数组相比会消耗更多的内存。

在实际链表之前我们需要实现的第一件事是单个节点的类: ​​​​​

class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

此实现仅存储两点数据 - 存储在节点 ( ) 中的值data和对下一个节点 ( ) 的引用next。

现在我们可以跳到实际的堆栈类本身。构造函数与之前的构造函数略有不同。它将只包含一个变量 - 对堆栈顶部节点的引用: ​​​​​​

class Stack:
    def __init__(self):
        self.top = None

正如预期的那样,该push()方法将一个新元素(在本例中为节点)添加到堆栈顶部: ​​​​​​

def push(self, item):
        node = Node(item)
        if self.top:
            node.next = self.top
        self.top = node

该pop()方法检查堆栈中是否有元素,如果堆栈不为空,则删除最上面的元素: ​​​​​​

def pop(self):
        if not self.top:
            raise Exception("Stack Underflow")
        item = self.top.data
        self.top = self.top.next
        return item

最后,该peek()方法只是从堆栈顶部读取元素的值(如果有的话): ​​​​

def peek(self):
    if not self.top:
        raise Exception("Stack is empty")
    return self.top.data

注意:两个类的接口Stack是相同的 - 唯一的区别是类方法的内部实现。这意味着您可以轻松地在不同的实现之间切换,而不必担心类的内部结构。

数组和链表之间的选择取决于应用程序的具体要求和约束。

如何使用 Python 的内置结构实现堆栈

对于许多开发人员来说,从头开始构建堆栈虽然具有教育意义,但可能不是在实际应用程序中使用堆栈的最有效方法。幸运的是,许多流行的编程语言都配备了自然支持堆栈操作的内置数据结构和类。

Python 作为一种多功能且动态的语言,没有专用的堆栈类。然而,它的内置数据结构,特别是模块中的列表和双端队列类collections,可以轻松地用作堆栈。

使用 Python 列表作为堆栈

append()由于 Python 列表的动态特性以及和 等方法的存在,Python 列表可以非常有效地模拟堆栈pop()。

  • 推送操作- 将元素添加到堆栈顶部就像使用以下append()方法一样简单:

  • stack = []
    stack.append('A')
    stack.append('B')
  • Pop 操作- 删除最上面的元素可以使用不带任何参数的方法来实现pop():

  • top_element = stack.pop()  # This will remove 'B' from the stack
  • 窥视操作可以使用负索引来访问顶部而不弹出:

  • top_element = stack[-1]  # This will return 'A' without removing it

使用集合模块中的deque类

deque(双端队列的缩写)类是堆栈实现的另一个多功能工具。它针对两端的快速追加和弹出进行了优化,使其堆栈操作比列表稍微高效一些。

  • 初始化:

  • from collections import deque
    stack = deque()
  • 推送操作- 与列表类似,append()使用方法:

  • stack.append('A')
    stack.append('B')
  • 弹出操作- 与列表类似,pop()方法执行以下工作:

  • top_element = stack.pop()  # This will remove 'B' from the stack
  • 查看操作- 方法与列表相同:

top_element = stack[-1]  # This will return 'A' without removing it
  • 虽然列表和双端队列都可以用作堆栈,但如果您主要将结构用作堆栈(从一端追加和弹出),由于deque其优化,速度可能会稍快一些。

  • 然而,对于大多数实际目的,除非处理性能关键的应用程序,Python 的列表应该足够了。

注意:本节深入探讨 Python 的类似堆栈行为的内置产品。当您手边拥有如此强大的工具时,您不一定需要重新发明轮子(通过从头开始实现堆栈)。

堆栈潜在的相关问题以及如何克服它们

虽然堆栈像任何其他数据结构一样具有令人难以置信的通用性和高效性,但它们也不能避免潜在的陷阱。在使用堆栈时必须认识到这些挑战并制定解决这些挑战的策略。在本节中,我们将深入探讨一些常见的堆栈相关问题,并探索解决这些问题的方法。

堆栈溢出

当尝试将元素推入已达到其最大容量的堆栈时,就会发生这种情况。在堆栈大小固定的环境中(例如在某些低级编程场景或递归函数调用中),这尤其是一个问题。

如果使用基于数组的堆栈,请考虑切换到动态数组或链表实现,它们会自行调整大小。防止堆栈溢出的另一个步骤是持续监视堆栈的大小,特别是在压入操作之前,并针对堆栈溢出提供明确的错误消息或提示。

如果由于过多的递归调用而导致堆栈溢出,请考虑迭代解决方案或在环境允许的情况下增加递归限制。

堆栈下溢

当尝试从空堆栈中弹出元素时会发生这种情况。为了防止这种情况发生,请在执行弹出或查看操作之前始终检查堆栈是否为空。返回清晰的错误消息或优雅地处理下溢,而不会导致程序崩溃。

在可接受的环境中,请考虑在从空堆栈弹出时返回一个特殊值以表示操作无效。

内存限制

在内存受限的环境中,即使动态调整堆栈大小(例如基于链表的堆栈),如果堆栈变得太大,也可能会导致内存耗尽。因此,请密切关注应用程序的整体内存使用情况和堆栈的增长。也许对堆栈的大小引入软上限。

线程安全问题

在多线程环境中,不同线程对共享堆栈的同时操作可能会导致数据不一致或意外行为。此问题的潜在解决方案可能是:

  • 互斥体和锁- 使用互斥体(互斥对象)或锁来确保在给定时间只有一个线程可以在堆栈上执行操作。

  • 原子操作- 如果环境支持,则利用原子操作来确保推送和弹出操作期间的数据一致性。

  • 线程本地堆栈- 在每个线程都需要其堆栈的情况下,请考虑使用线程本地存储为每个线程提供单独的堆栈实例。

总结

虽然堆栈确实很强大,但了解其潜在问题并积极实施解决方案将确保应用程序的健壮和无错误。认识到这些陷阱就成功了一半,另一半就是采用最佳实践来有效解决这些问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1188338.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

山西电力市场日前价格预测【2023-11-09】

日前价格预测 预测说明: 如上图所示,预测明日(2023-11-09)山西电力市场全天平均日前电价为369.84元/MWh。其中,最高日前电价为784.47元/MWh,预计出现在17: 45。最低日前电价为158.90元/MWh,预计…

漏刻有时百度地图API实战开发(6)多个标注覆盖层级导致不能响应点击的问题

漏刻有时百度地图API实战开发(1)华为手机无法使用addEventListener click 的兼容解决方案漏刻有时百度地图API实战开发(2)文本标签显示和隐藏的切换开关漏刻有时百度地图API实战开发(3)自动获取地图多边形中心点坐标漏刻有时百度地图API实战开发(4)显示指定区域在移动端异常的解…

国产猫罐头可以作为长期主食吗?口碑好的顶级猫罐头推荐

我一直在分析和尝试国产猫罐头,我家猫已经吃了几十款了。今天,我想和大家分享一些关于国产猫罐头的经验和心得。 近年来,国产宠粮市场呈现出爆发趋势,各个猫粮商在配方、营养数据和包装上展开了激烈的角逐,无一不让我…

和数链“分布式存储”技术结合隐私计算让数据更安全

存储是IT业的核心技术,全球存储行业历经半个世纪的洗礼,在技术和需求相互促进的演变下沧桑变幻,经历桌面级存储、企业级存储、云存储多次迭代变迁。 目前的存储方式主要是“大数据中心”等集中式存储,随着数据规模和复杂度的迅速…

安卓RadioButton设置图片大小

RadioButton都不陌生,一般我们都会设置图片在里面,这就涉及一个问题,图片的大小。如果图片过大,效果很不理想。搜了很多方法,都不理想。无奈只能自己研究了 代码如下: 1,一个简单的 RadioButt…

SOLIDWORKS跨版本升级后卡顿、运行缓慢...如何处理这些情况?

不知道各位有没有发生过以下情况: 以前一直使用的solidworks版本是旧版比如2018版,但过了几年把solidworks升级到最新版后,可能会出现以前版本保存的内容在新版本里打开后打开速度缓慢或者进行更改后保存会需要很长时间才能保存完毕甚至是会…

Python 框架学习 Django篇 (九) 产品发布、服务部署

我们前面编写的所有代码都是在windows上面运行的,因为我们还处于开发阶段 当我们完成具体任务开发后,就需要把我们开发的网站服务发布给真正的用户 通常来说我们会选择一台公有云服务器比如阿里云ecs,现在的web服务通常都是基于liunx操作系统…

Hbuilder打包项目为h5

Hbuilder打包项目为h5 manifest.json 配置 修改 web 配置下的 页面标题、路由模式、运行的基础路径 发行 H5 发行 填入网站标题和网站域名 编译 编译完成之后存放在 unpackage/dist/build/h5 目录下

OpenCV 输出文本

PutText() 输出文本 OpenCV5 将支持中文字符的输出, 当前版本OpenCV4原生不支持, 可以使用Contrib包FreeType方式实现, 不过比较麻烦.为了省事, 也可以通过将Mat转成bitmap,然后使用GDI方式输出中文字符. 示例代码 /// <summary>/// OpenCV暂时不能支持中文字符输出,显示…

卓越进行时 | 赛宁助力职业院校实践“岗课赛证训创”育人模式

11月7日&#xff0c;赛宁网安邀请南京城市职业学院网络安全学科师生走进网络安全卓越中心&#xff0c;为大一新生提供“职业导学” 和“岗位认知”的综合性实践课程&#xff0c;帮助学生提升对于网络安全专业和未来职业的认知水平&#xff0c;进一步深化校企合作人才培养的持续…

centos中安装的goland配置sdk报错:所选的目录不是Go SDK的有效主路经

选中目录后一直报错&#xff1a; 正确的位置&#xff1a; 原因竟然是使用 解压go1.21.4.linux-amd64.tar.gz 包出来&#xff0c;少了scr和test目录&#xff0c;重新解压后可以正确设定SDK主目录。 有同样问题的可以确认一下。 tar -C /usr/local -zxvf go1.19.2.linux-amd64.…

rabbitmq下载安装教程

1.首先需要下载erlang和rabbitmq安装包&#xff1a; 官网下载比较慢&#xff0c;通过网盘下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1fM2BrJqefyzUDZD4tfZLIg 提取码&#xff1a;5hsu 2.安装&#xff0c;傻瓜式安装就可以&#xff0c;可以自定义自己要安装的目…

封装一个中间大两头小的轮播图(vue-awesome-swiper、vue2)

需求 先看效果图 对vue来说&#xff0c;element-UI是有相应的轮播组件&#xff08;走马灯&#xff09;的&#xff0c;但相对简单的 如上面的两头大中间小轮播&#xff0c;element上文档这款很类似&#xff0c;但不适用&#xff0c;因为卡片之间底层移动和间距是依赖js实现的…

【Transformer从零开始代码实现 pytoch版】(二)Encoder编码器组件:mask+attention+feed forward+addnorm

Encoder组件 编码器部分&#xff1a; 由N个编码器层堆叠而成每个编码器层由两个子层连接结构组成第一个子层连接结构包括一个多头自注意力子层和规范化层以及一个残差连接第二个子层连接结构包括一个前馈全连接子层和规范化层以及一个残差连接 &#xff08;1&#xff09;Mask…

Python 标准库 subprocess 模块详解

1. Subprocess模块介绍 1.1 基本功能 subprocess 模块&#xff0c;允许生成新的进程执行命令行指令&#xff0c;python程序&#xff0c;以及其它语言编写的应用程序, 如 java, c,rust 应用等。subprocess可连接多个进程的输入、输出、错误管道&#xff0c;并且获取它们的返回…

医院检验信息管理系统源码 医院LIS系统源码 云LIS源码 区域LIS源码

医院检验信息管理系统源码 医院LIS系统源码 云LIS源码 区域LIS源码 医院检验信息管理系统&#xff0c;利用计算机网络技术、数据存储技术、快速处理技术&#xff0c;对检验科进行全方位信息化管理&#xff0c;使检验科达到自动化运行&#xff0c;信息化管理和无纸化办公的目的…

山西电力市场日前价格预测【2023-11-10】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-11-10&#xff09;山西电力市场全天平均日前电价为480.16元/MWh。其中&#xff0c;最高日前电价为710.56元/MWh&#xff0c;预计出现在18: 00。最低日前电价为388.44元/MWh&#xff0c;预计…

double类型数相减有小数误差问题

相减有误差 BigDecimal消除误差

12.(vue3.x+vite)组件间通信方式之$attrs与$listeners

前端技术社区总目录(订阅之前请先查看该博客) 示例效果 在vue3中的$attrs的变化 $ listeners已被删除合并到$ attrs中。 $ attrs现在包括class和style属性。 也就是说在vue3中$ listeners不存在了。vue2中$listeners是单独存在的。 在vue3 $attrs包括class和style属性, vue…

程序员的护城河:构建数字世界的守护者

目录 前言1 持续学习的愿望和能力2 与他人沟通和合作的能力3 追求技术的深度和广度4 具备分享的精神结语 前言 在数字化时代&#xff0c;程序员是现代社会的护城河。他们的工作不仅是构建应用程序和系统&#xff0c;更是为保障系统安全、数据防护以及网络稳定发挥着至关重要的…