Python堆栈详细介绍

                                                          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 的类似堆栈行为的内置产品。当您手边拥有如此强大的工具时,您不一定需要重新发明轮子(通过从头开始实现堆栈)。

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

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

堆栈溢出

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

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

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

堆栈下溢

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

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

内存限制

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

线程安全问题

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

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

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

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

总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

web3通过antd 在React dapp中构建订单组件基本结构

上文web3 dapp React项目引入 antd 对 balance 用户token信息组件进行样式改造 中 我们导入 antd组件 算是比较完整的编写了用户资产组件 那么 今天开始 我们就要说订单组件了 这个就会比之前的复杂很多 我们还是先开环境 ganache 终端执行 ganache -d然后 将合约 发布到区块链…

使用promise创建一个同步事件驱动api

使用promise创建一个同步事件驱动api 事件驱动体系结构(EDA)是一种强大的方法,可以在网络上构建松散耦合、性能好、可伸缩的应用程序。它支持非常多功能,如推送通知、协作编辑和多人操作,以及其他实时交互。 但有时模型与我们开发人员需要的…

【树的存储结构,孩子链表】

文章目录 树和森林树的存储结构孩子链表 树和森林 森林:是m(m>0)棵互不相交的树的集合。 树的存储结构 1.双亲表示法 实现:定义结构数组存放树的结点,每个结点含两个域。 数据域:存放结点本身信息。 双亲域:指…

虚假内容检测,谣言检测,不实信息检测,事实核查;纯文本,多模态,多语言;数据集整理

本博客系博主个人理解和整理所得,包含内容无法详尽,如有补充,欢迎讨论。 这里只提供数据集相关介绍和来源出处,或者下载地址等,因版权原因不提供数据集所含的元数据。如有需要,请自行下载。 “Complete d…

亚马逊云科技海外服务器初体验

目录 前言亚马逊云科技海外服务器概述注册使用流程实例创建性能表现用户体验服务支持初体验总结 前言 随着云原生技术的飞速发展,越来越多的企业和开发者选择云服务器来作为自己的使用工具,云原生技术的发展也促进了云服务厂商的产品发展,所…

Leetcode Hot 100之四:283. 移动零+11. 盛最多水的容器

283.移动零 题目: 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0] …

算法通关村第七关-黄金挑战二叉树迭代遍历

大家好我是苏麟 , 今天带来二叉树的迭代遍历 . 二叉树的迭代遍历 前序编列 描述 : 给你二叉树的根节点 root ,返回它节点值的 前序 遍历。 题目 : LeetCode 二叉树的前序遍历 : 144. 二叉树的前序遍历 分析 : 前序遍历是中左右,如果还有左子树就一…

一款基于.Net开发、开源、支持多平台云存储文件管理器

目录 01 项目简介02 项目代码03 部分截图04 项目地址 今天给大家推荐一款基于基于.Net开发、开源的,支持多平台的云存储文件管理器。 01 项目简介 Camelotia是一款云存储文件管理器,基于.Net UI框架和ReactiveUI框架开发的,目前支持的平台有…

AI:75-基于生成对抗网络的虚拟现实场景增强

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

Bitget Wallet:使用 Base 链购买 ETH 的简明教程

Base 链是一种 Layer 2(L2)公链,它可以为用户提供以太坊(ETH)代币,而 Bitget Wallet 是一款多功能加密货币钱包,支持 Base 链以及其他主要区块链。

进行 “最佳价格查询器” 的开发

前置条件 public class Shop {private final String name;private final Random random;public Shop(String name) {this.name name;random new Random(name.charAt(0) * name.charAt(1) * name.charAt(2));}public double getPrice(String product) {return calculatePrice…

SpringBoot自动配置的原理篇,剖析自动配置原理;实现自定义启动类!附有代码及截图详细讲解

SpringBoot 自动配置 Condition Condition 是在Spring 4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建 Bean 操作 思考:SpringBoot是如何知道要创建哪个Bean的?比如SpringBoot是如何知道要创建RedisTemplate的?…

剖析WPF模板机制的内部实现

剖析WPF模板机制的内部实现 众所周知,在WPF框架中,Visual类是可以提供渲染(render)支持的最顶层的类,所有可视化元素(包括UIElement、FrameworkElment、Control等)都直接或间接继承自Visual类。…

单例模式 rust和java的实现

文章目录 单例模式介绍应用实例:优点使用场景 架构图JAVA 实现单例模式的几种实现方式 rust实现 rust代码仓库 单例模式 单例模式(Singleton Pattern)是最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建…

【第2章 Node.js基础】2.4 Node.js 全局对象...持续更新

什么是Node.js 全局对象 对于浏览器引擎来说,JavaScript 脚本中的 window 是全局对象,而Node.js程序中的全局对象是 global,所有全局变量(除global本身外)都是global 对象的属性。全局变量和全局对象是所有模块都可以调用的。Node.is 的全局…

docker部署mongodb

1:拉去momgodb镜像 2:拉去成功后,通过docker-compose.yml配置文件启动mongodb,docker-compose.yml配置如下 version: 3.8 services:mongodb-1:container_name: mongodbimage: mongo ports:- "27017:27017"volumes:- G:…

TCP/IP详解

TCP/IP详解 一、网络基础1.TCP/IP网络分层2.IP地址和端口号3.封装与分用4.客户-服务端模型 二、链路层1.以太网IEEE802封装2.环回接口 Loopback Interface3.最大传输单元MTU和路径MTU 三、网络层 - IP1.IP首部的关键信息2.IP路由选择3.子网寻址和子网掩码4.ICMP和IGMP 四、传输…

python爬虫hook定位技巧、反调试技巧、常用辅助工具

一、浏览器调试面板介绍 二、hook定位、反调试 Hook 是一种钩子技术,在系统没有调用函数之前,钩子程序就先得到控制权,这时钩子函数既可以加工处理(改变)该函数的执行行为,也可以强制结束消息的传递。简单…