数据结构与算法之美 | 栈

  • 栈结构:后进者先出,先进者后出

  • 栈是一种“操作受限”的线性表

  • 当某个数据集合只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性,这时我们就应该首选“栈”这种数据结构

栈的实现

  • 使用数组实现:顺序栈
class ArrayStack:
    """使用数组实现一个顺序栈"""

    def __init__(self):
        '''
        初始化顺序栈

        参数:
            无

        返回值:
            无
        '''
        
        self.items = []

    def push(self, item):
        '''
        入栈操作

        参数:
            item (任意类型): 待入栈的值

        返回值:
            无
        '''
        self.items.append(item)

    def pop(self):
        '''
        出栈操作

        参数:
            无

        返回值:
            待出栈的值
        '''
        if not self.is_empty():
            return self.items.pop()

    def is_empty(self):
        '''
        检查栈是否为空

        参数:
            无

        返回值:
            bool: 如果栈为空则返回True,否则返回False
        '''
        return len(self.items) == 0

  • 使用链表实现:链式栈
class Node:
    '''维护链表节点'''
    def __init__(self, value):
        '''
        初始化链表节点

        参数:
            value (任意类型): 节点的值

        返回值:
            无
        '''
        # 节点的值
        self.value = value
        # 指向下一个节点的指针
        self.next = None

class Stack:
    '''进行出栈或入栈操作'''
    def __init__(self):
        '''
        初始化栈

        参数:
            无

        返回值:
            无
        '''
        # 栈顶节点
        self.top = None

    def push(self, value):
        '''
        入栈操作

        参数:
            value (任意类型): 要入栈的值

        返回值:
            无
        '''
        if self.top is None:
            self.top = Node(value)
        else:
            # 创建新节点
            new_node = Node(value)
            # 将新节点指向当前的栈顶节点
            new_node.next = self.top
            # 更新栈顶节点为新节点
            self.top = new_node

    def pop(self):
        '''
        出栈操作

        参数:
            无

        返回值:
            出栈的节点的值
        '''
        if self.top is None:
            return None
        else:
            # 弹出栈顶节点
            popped_node = self.top
            # 更新栈顶节点为下一个节点
            self.top = self.top.next
            # 将弹出的节点从链表中断开
            popped_node.next = None
            # 返回弹出节点的值
            return popped_node.value

栈的应用

函数调用栈

操作系统给每个线程分配了一块独立的内存空间,这块内存被组织成“栈”这种结构, 用来存储函数调用时的临时变量。每进入一个函数,就会将临时变量作为一个栈帧1入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈。

def main():
    '''
    主函数,用于执行程序的主要逻辑

    参数:
        无

    返回值:
        int: 表示程序执行结果的返回值
    '''
    a = 1
    ret = 0
    res = 0

    # 调用add函数,将返回值赋给ret变量
    ret = add(3, 5)

    # 计算a和ret的和,将结果赋给res变量
    res = a + ret

    # 打印res的值
    print(res)

    return 0


def add(x, y):
    '''
    将两个数字相加

    参数:
        x (int): 第一个数字
        y (int): 第二个数字

    返回值:
        int: 两个数字的和
    '''
    sum = 0

    # 计算x和y的和,将结果赋给sum变量
    sum = x + y

    return sum


if __name__ == '__main__':
    # 调用main函数
    main()

img

能否使用其他数据结构实现函数调用功能?

  • 虽然其他数据结构也可以用来保存临时变量,但是它们的实现可能不如栈那么高效
  • 例如,使用链表来保存上下文信息,需要在插入和删除元素时遍历链表,时间复杂度为O(n),而使用栈的时间复杂度为O(1)。因此,栈是最常用的函数调用栈的实现方式。

表达式求值

编译器通过两个栈来实现的。其中一个保存操作数的栈,另一个是保存运算符的栈。

  1. 我们从左向右遍历表达式,当遇到数字,我们就直接压入操作数栈;
  2. 当遇到运算符,就与运算符栈的栈顶元素进行比较。如果比运算符栈顶元素的优先级高,就将当前运算符压入栈;
  3. 如果比运算符栈顶元素的优先级低或者相同,从运算符栈中取栈顶运算符,从操作数栈的栈顶取 2 个操作数,然后进行计算,再把计算完的结果压入操作数栈,继续比较。

img

检查括号是否匹配

假设表达式中只包含三种括号:

  • 圆括号 ()

  • 方括号[]

  • 花括号{}

它们可以任意嵌套。比如,{[] ()[{}]}[{()}([])]等都为合法格式,而{[}()][({)]为不合法的格式。

那么如何检查括号是否合法?

  1. 我们用栈来保存未匹配的左括号,从左到右依次扫描字符串。

  2. 当扫描到左括号时,则将其压入栈中;当扫描到右括号时,从栈顶取出一个左括号。如果能够匹配,比如 () 匹配,[]匹配,{}匹配,则继续扫描剩下的字符串。如果扫描的过程中,遇到不能配对的右括号,或者栈中没有数据,则说明为非法格式。

  3. 当所有的括号都扫描完成之后,如果栈为空,则说明字符串为合法格式;否则,说明有未匹配的左括号,为非法格式。

实现浏览器的前进和后退功能

  • 使用两个栈,X 和 Y。
  • 我们把首次浏览的页面依次压入栈 X,当点击后退按钮时,再依次从栈 X 中出栈,并将出栈的数据依次放入栈 Y。当我们点击前进按钮时,我们依次从栈 Y 中取出数据,放入栈 X 中。
  • 当栈 X 中没有数据时,那就说明没有页面可以继续后退浏览了。当栈 Y 中没有数据,那就说明没有页面可以点击前进按钮浏览了。

JVM中的堆栈和栈的区别是什么?

JVM 中的“栈”和数据结构中的“栈”是类似的,都是一种后进先出(LIFO)的数据结构。但是它们的实现和使用方式是不同的。

在JVM中,每个线程都有一个私有的栈,用于存储方法调用时的局部变量和操作数栈。当一个方法被调用时,会在栈上创建一个新的栈帧,用于存储该方法的局部变量和操作数栈等信息。当方法返回时,该方法对应的栈帧就会被销毁。因此,JVM中的“栈”主要用于方法调用和返回,以及存储线程私有的数据。

在计算机科学中,栈通常指的是一种数据结构,它是一种后进先出(LIFO)的数据结构,只能在栈顶进行插入和删除操作。栈通常用于函数调用时保存临时变量和返回地址,以及递归等算法实现中

Leetcode:栈习题

题目:

  • 20
  • 155
  • 232
  • 844
  • 224
  • 682
  • 496

  1. 每当一个函数被调用时,都会创建一个新的栈帧(Stack Frame),用于保存该函数的局部变量、参数、返回地址等信息 ↩︎

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

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

相关文章

初探图神经网络——GNN

title: 图神经网络(GNN) date: tags: 随笔知识点 categories:[学习笔记] 初探图神经网络(GNN) 文章来源:https://distill.pub/2021/gnn-intro/ 前言:说一下为什么要写这篇文章,因为自己最近一直听说“图神经网络”,但是一直不了…

pycharm使用之torch_sparse安装

正式安装之前要先查看一下torch的版本 一、查看torch版本 1、winR ,输入cmd 2、输入python 3、 输入import torch,然后输入torch.__version__,最后回车 可以看到我的torch版本是1.10.0 二、下载合适的torch_sparse版本 1、打开链接 https…

接口反应慢优化

遇到某个功能,页面转圈好久,需要优化 1.F12 查看接口时间 2.看参数 总共耗时9.6s Waiting for sercer response 时间是2秒 Content Download 7秒 慢在Content Download F12查看接口响应 显示Failed to load response data:Request content was e…

spark入门 高可用部署HA(五)

一、standalone基于修改部署 https://blog.csdn.net/weixin_43205308/article/details/131070277?spm1001.2014.3001.5501 二、安装ZOOKEEPER zookeeper 安装下载与集群 三、修改conf下的spark-env.sh vim conf/spark-env.sh注释以下内容(根据自己环境修改&am…

visual studio 2022,ADO.NET 实体数据模型添加 sqlite数据库对象

文章目录 前言前期环境博客github 文档解析文件安装说明文件下载省流版nuget环境配置成功标志sqlite连接测试 前言 我们知道ADO.NET 实体数据模型特别适合动态开发数据库。因为ADO.NET可以使用DB First 开发 我们在开发一个程序的时候,经常会动态更新数据库字段&a…

算法模板(3):搜索(4):高等图论

高等图论 有向图的强连通分量 相关概念 强连通分量:Strongly Connected Component (SCC).对于一个有向图顶点的子集 S S S,如果在 S S S 内任取两个顶点 u u u 和 v v v,都能找到一条 u u u 到 v v v 的路径,那么称 S S…

C++多态和文件读写

C黑马,每天1.5倍速2个视频(1小时),看到9月1日完成314个视频 目录 🔑多态 🌳基本语法 🌳原理剖析 🌳案例1 -- 计算器类 🌳纯虚函数和抽象类 🌳案例2 --…

redis知识复习

redis知识复习 redis基础知识一. redis的认识1. 非关系型数据库 与 传统数据库 的区别2. 安装redis并设置自启动3. 熟悉命令行客户端4. 熟悉图形化工具RDM 二. redis的命令与数据结构1. 数据结构介绍2. redis通用命令(熟练掌握) 三. redis的Java客户端1.…

SpringBoot整合Flyway实现数据库的初始化和版本管理

文章目录 一、Flyway1、介绍2、业务痛点3、个人理解 二、SpringBoot整合flyway1、整合2、SQL文件命名3、版本号校验算法4、工作流程5、注意事项 一、Flyway 1、介绍 Flyway 是一款开源的数据库版本管理工具。它可以很方便的在命令行中使用,或者在Java应用程序中引入…

【MySQL】数据表的基本操作

目录 1. 创建表 2. 创建表案例 2.1 创建一个users表 2.2 查看表结构 2.3 修改表 3. 删除表 MySQL🌷 1. 创建表 语法: CREATE TABLE table_name (field1 datatype,field2 datatype,field3 datatype ) character set 字符集 collate 校验规则 engine 存储…

chatgpt赋能python:如何升级Python的pip版本

如何升级Python的pip版本 如果你使用Python来进行程序开发,那么你一定需要用到pip,它是Python的包管理器,用于安装和管理各种Python库。 不过,一旦你开始使用pip,你可能会遇到一个问题:你的pip版本可能会…

几种技巧让大模型(ChatGPT、文心一言)帮你提高写代码效率!

代码神器 自从大模型推出来之后,似乎没有什么工作是大模型不能做的。特别是在文本生成、文案写作、代码提示、代码生成、代码改错等方面都表现出不错的能力。下面我将介绍运用大模型写代码的几种方式,帮助程序员写出更好的代码!(…

利用AI点亮副业变现:5个变现实操案例的启示

AI变现副业实操案例 宝宝起名服务AI科技热点号头像壁纸职业头像收徒:萌娃头像定制头像平台挂载 小说推广号流量营销号百家号AI共创计划公众号流量主 知识付费知识星球小报童: 整体思维导图: 在这里先分享五个实操案例: 宝宝起名服务AI科技热…

cvte 前端一面 凉经

cvte 前端一面 凉经 原文面试题地址:https://www.nowcoder.com/discuss/353159272857018368?sourceSSRsearch 1. vuex原理 和vuerouter的原理差不多 2. vuerouter的原理 ​ 首先在main.js中,import router from ‘./router’ 引入在router文件夹下面…

学习WooCommerce跨境电商社交媒体营销

WooCommerce 长期以来一直为电子商务店主提供多样化的服务。大约 500 万家商店啓用安装了免费的 WooCommerce 插件。 官方 WooCommerce 插件从 WordPress.org 下载了161,908,802次,并且还在增加。 超过5,106,506 个网站正在使用 WooCommerce。 本文网址: https…

一文搞懂什么是Docker

一、什么是Docker 微服务虽然具备各种各样的优势,但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中,依赖的组件非常多,不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署,环境不一定一致,会遇…

LVS+Keepalived 群集

目录 一、keepalived概述 1.keepalived工作原理 2.keepalived体系主要模块及其作用 3.判断服务器主备,及如何配置浮动IP 二、keepalived的抢占与非抢占模式 三、部署LVSkeepalived 1.配置负载调度器(主备相同) 1.1配置keepalived&…

NVM安装教程

我是小荣,给个赞鼓励下吧! NVM安装教程 简介 nvm 是node.js的版本管理器,设计为按用户安装,并按 shell 调用。nvm适用于任何符合 POSIX 的 shell(sh、dash、ksh、zsh、bash),特别是在这些平台…

chatgpt赋能python:Python编程:如何删除前面的代码?

Python编程:如何删除前面的代码? 在Python编程中,我们有时会需要删除之前写的一些代码,以便更好地组织我们的代码结构和逻辑。那么,Python中如何删除前面的代码呢?在本文章中,我们将为您详细介…

工程训练 -江苏海洋大学-mooc-最终答案

这不点赞评论一下嘛???呜呜呜 判断题(共217道) 1.舂实模样周围及砂箱边或狭窄部分的型砂,通常采用砂舂的平头端舂砂。 2.造型时,分型面上通常使用的是面砂,覆盖模样的则使用背砂。 3…