数据结构 | 基本数据结构——栈

目录

一、线性数据结构

二、栈

2.1 何谓栈

2.2 栈抽象数据类型

2.3 用Python实现栈

2.4 匹配括号

2.5 普通情况:匹配符号

2.6 将十进制数转换成二进制数

3.7 前序、中序和后序表达式

3.7.1 从中序到后序的通用转换法

3.7.2 计算后序表达式


一、线性数据结构

栈、队列、双端队列和列表都是有序的数据集合,其元素的顺序取决于添加顺序或移除顺序。一旦某个元素被添加进来,它与前后元素的相对位置将保持不变。这样的数据集合经常被称为线性数据结构。

线性数据结构可以看作有两端。真正区分线性数据结构的是元素的添加方式和移除方式,尤其是添加操作和移除操作发生的位置。举例来说,某个数据结构可能只允许在一端添加新元素,有些则允许从任意一端移除元素。

二、栈

2.1 何谓栈

栈有时也被称作“下推栈 ”。它是有序集合,添加操作和移除操作总发生在同一端,即“顶端”,另一端则被称为“底端”。

栈中的元素离底端越近,代表其在栈中的时间越长,因此栈的底端具有非常重要的意义。最新添加的元素将被最先移除。这种排序原则被称作LIFO,即后进先出。它提供了一种基于在集合中的时间来排序的方式。最近添加的元素靠近顶端,旧元素靠近底端。

2.2 栈抽象数据类型

栈抽象数据类型由下面的结构和操作定义。如前所述,栈是元素的有序集合,添加操作与移除操作都发生在其顶端。栈的操作顺序是LIFO,它支持以下操作。

  • Stack()创建一个空栈。它不需要参数,且会返回一个空栈。
  • push(item)将一个元素添加到栈的顶端。它需要一个参数item,且无返回值。
  • pop()将栈顶端的元素移除。它不需要参数,但会返回顶端的元素,并且修改栈的内容。
  • peek()返回栈顶端的元素,但是并不移除该元素。它不需要参数,也不会修改栈的内容。
  • isEmpty()检查栈是否为空。它不需要参数,且会返回一个布尔值。
  • size()返回栈中元素的数目。它不需要参数,且会返回一个整数。

2.3 用Python实现栈

抽象数据类的实现被称为数据结构。

以下代码是栈的实现,它假设列表的尾部是栈的顶端。当栈增长时(即进行push操作),新的元素会被添加到列表的尾部,pop操作同样会修改这一端。

class Stack:def __init__(self):self.items=[]def isEmpty(self):return self.items==[]def push(self,item):self.items.append(item)def pop(self):return self.items.pop()def peek(self):return self.items[len(self.items)-1]def size(self):return len(self.items)

接下来展示上述代码中的栈操作及其返回结果。

s=Stack()
print(s.isEmpty())s.push(4)
s.push('dog')
print(s.peek())s.push(True)
print(s.size())print(s.isEmpty())s.push(8.4)
print(s.pop())print(s.pop)print(s.size)

也可以选择将列表的头部作为栈的顶端。不过在这种情况下,便无法直接使用pop方法和append方法,而必须使用pop方法和insert方法显式地访问下标为0的元素,即列表中的第1个元素。代码如下:

class Stack:def __init__(self):self.items=[]def isEmpty(self):return self.items==[]def push(self,item):self.items.insert(0,item)def pop(self):return self.items.pop(0)def peek(self):return self.items[0]def size(self):return len(self.items)

尽管上述两种实现都可行,但是二者在性能方面肯定有差异。append方法和pop()方法的时间复杂度都是O(1),这意味着不论栈中有多少个元素,第一种实现中的push操作和pop操作都会在恒定时间内完成。第二种实现的性能则受制于栈中的元素个数,这是因为insert(0)和pop(0)的时间复杂度都是O(n),元素越多越慢。

2.4 匹配括号

由一个空栈开始,从左到右依次处理括号。如果遇到左括号,便通过push操作将其加入栈中,以此表示稍后需要有一个与之匹配的右括号。反之,如果遇到右括号,就调用pop操作。只要栈中的所有左括号都能遇到与之匹配的右括号,那么整个括号串就是匹配的;如果栈中有任何一个左括号找不到与之匹配的右括号,则括号串就是不匹配的。在处理完匹配的括号串之后,栈应该是空的。代码如下:

from pythonds.basic import Stackdef parChecker(symbolString):s=Stack()balanced=Trueindex=0while index<len(symbolString) and balanced:symbol=symbolString[index]if symbol=="(":s.push(symbol)else:if s.isEmpty():balanced=Falseelse:s.pop()index=index+1if balanced and s.isEmpty():return Trueelse:return False

2.5 普通情况:匹配符号

from pythonds.basic import Stackdef parChecker(symbolString):s=Stack()balanced=Trueindex=0while index<len(symbolString) and balanced:symbol=symbolString[index]if symbol in "([{":s.push(symbol)else:if s.isEmpty():balanced=Falseelse:top=s.pop()if not matches(top,symbol):balanced=Falseindex=index+1if balanced and s.isEmpty():return Trueelse:return Falsedef matches(open,close):opens="([{"closers=")]}"return opens.index(open)==closers.index(close)

2.6 将十进制数转换成二进制数

将十进制数转换成二进制数采用的是“除以2”算法。“除以2”算法假设待处理的整数大于0.它用一个简单的循环不停地将十进制数除以2,并且记录余数。第一次除以2的结果能够用于区分偶数和奇数。如果是偶数,则余数为0,因此个位上的数字是0;如果是奇数,则余数为1,因此个位上的数字是1.可以将要构建的二进制数看成一系列数字;计算出的第一个余数是最后一位。这体现出了反转特性,因此用栈来解决问题是合理的。

from pythonds.basic import Stackdef divideBy2(decNumber):remstack=Stack()while decNumber>0:rem=decNumber%2remstack.push(rem)decNumber=decNumber//2binString=""while not remstack.isEmpty():binString=binString+str(remstack.pop())return binString

将十进制数转换成任意进制数的代码如下:

from pythonds.basic import Stackdef baseConverter(decNumber,base):digits="0123456789ABCDEF"remstack=Stack()while decNumber>0:rem=decNumber%baseremstack.push(rem)decNumber=decNumber//basenewString=""while not remstack.isEmpty():newString=newString+digits[remstack.pop()]return newString

3.7 前序、中序和后序表达式

3.7.1 从中序到后序的通用转换法

假设中序表达式是一个以空格分隔的标记串。其中,运算符标记有*、/、+和-,括号标记有(和),操作数标记有A、B、C等。下面的步骤会生成一个后序标记串。

(1)创建用于保存运算符的空栈opstack,以及一个用于保存结果的空列表。

(2)从左往右扫描这个标记列表。

  • 如果标记是操作数,将其添加到结果列表的末尾。
  • 如果标记是左括号,将其压入opstack栈中。
  • 如果标记是右括号,反复从opstack栈中移除元素,直到移除对应的左括号。将从栈中取出的每一个运算符都添加到结果列表的末尾。
  • 如果标记的是运算符,将其压入opstack栈中。但是,在这之前,需要先从栈中取出优先级更高或相同的运算符,并将它们添加到列表的末尾。

(3)当处理完输入表达式以后,检查opstack。将其中所有残留的运算符全部添加到结果列表的末尾。

为了在Python中实现这一算法,我们使用一个叫做prec的字典来保存运算符的优先级值。该字典把每一个运算符都映射成一个整数。通过比较对应的整数,可以确定运算符的优先级。左括号的优先级值最小。这样一来,任何与左括号比较的运算符都会被压入栈中。我们也将导入string模块,它包含一系列预定义变量。本例使用一个包含所有大写字母的字符串(string.ascii_uppercase)来代表所有可能出现的操作数。代码如下:

from pythonds.basic import Stack
import stringdef infixToPostfix(infixexpr):prec={}prec['*']=3prec['/']=3prec['+']=2prec['-']=2prec['(']=1opStack=Stack()postfixList=[]for token in infixexpr:if token in string.ascii_uppercase:postfixList.append(token)elif token=='(':opStack.push(token)elif token==')':topToken=opStack.pop()while topToken != '(':postfixList.append(topToken)topToken=opStack.pop()else:while (not opStack.isEmpty()) and (prec[opStack.peek()]>=prec[token]):postfixList.append(opStack.pop())opStack.push(token)while not opStack.isEmpty():postfixList.append(opStack.pop())return " ".join(postfixList)print(infixToPostfix("(A+B)*(C+D)"))

3.7.2 计算后序表达式

假设后序表达式是一个以空格分隔的标记串。其中,运算符标记有*、/、+、-,操作数标记是一位的整数值。结果是一个整数。

(1)创建空栈operandStack.

(2)使用字符串方法split将输入的后序表达式转换成一个列表。

(3)从左往右扫描这个标记列表。

  • 如果标记是 操作数,将其转换成整数并且压入operandStack栈中。

如果标记是运算符,从operandStack栈中取出两个操作数。第一次取出右操作数,第二次取出左操作数。进行相应的算数运算,然后将运算结果压入operandStack栈中。

(4)当处理完输入表达式时,栈中的值就是结果。将其从栈中返回。

from pythonds.basic import Stack
def postfixEval(postfixExpr):operandStack=Stack()tokenList=postfixExpr.split()for token in tokenList:if token in "0123456789":operandStack.push(int(token))else:operand2=operandStack.pop()operand1=operandStack.pop()result=doMath(token,operand1,operand2)operandStack.push(result)return operandStack.pop()def doMath(op,op1,op2):if op=="*":return op1*op2elif op=="/":return op1/op2elif op=="+":return op1+op2else:return op1-op2

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

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

相关文章

[内网渗透]SUID提权

文章目录 [内网渗透]SUID提权0x01.什么是SUID&#xff1f;0x02.如何设置SUID&#xff1f;0x03.查找属主为root的SUID文件0x04.进行SUID提权1.find提权2.vim/vi/vim.tiny 以root权限修改文件3.bash提权4.less/more执行系统命令5.nano以root权限修改文件6.awk执行系统命令7.cp以r…

开源计算机视觉库OpenCV详解

目录 1、概述 2、OpenCV详细介绍 2.1、OpenCV的起源 2.2、OpenCV开发语言 2.3、OpenCV的应用领域 3、OpenCV模块划分 4、OpenCV源码文件结构 4.1、根目录介绍 4.2、常用模块介绍 4.3、CUDA加速模块 5、OpenCV配置以及Visual Studio使用OpenCV 6、关于Lena图片 7、…

LLM-Blender:大语言模型也可以进行集成学习

最近在看arxiv的时候发现了一个有意思的框架&#xff1a;LLM-Blender&#xff0c;它可以使用Ensemble 的方法来对大语言模型进行集成。 官方介绍如下&#xff1a;LLM-Blender是一个集成框架&#xff0c;可以通过利用多个开源大型语言模型(llm)的不同优势来获得始终如一的卓越性…

TCP如何保证服务的可靠性

TCP如何保证服务的可靠性 确认应答超时重传流量控制滑动窗口机制概述发送窗口和接收窗口的工作原理几种滑动窗口协议1比特滑动窗口协议&#xff08;停等协议&#xff09;后退n协议选择重传协议 采用滑动窗口的问题&#xff08;死锁可能&#xff0c;糊涂窗口综合征&#xff09;死…

ESP32 官方AT固件编译(从零开始环境搭建到编译完成全过程)

1、下载VMware免费版 https://download3.vmware.com/software/WKST-PLAYER-1702/VMware-player-full-17.0.2-21581411.exe 2、下载Ubuntu &#xff08;ubuntu-22.04.2-desktop-amd64.iso&#xff09;** https://releases.ubuntu.com/jammy/ubuntu-22.04.2-desktop-amd64.iso…

IDE/mingw下动态库(.dll和.a文件)的生成和部署使用(对比MSVC下.dll和.lib)

文章目录 概述问题的产生基于mingw的DLL动态库基于mingw的EXE可执行程序Makefile文件中使用Qt库的\*.a文件mingw下的*.a 文件 和 *.dll 到底谁起作用小插曲 mingw 生成的 \*.a文件到底是什么为啥mingw的dll可用以编译链接过程转换为lib引导文件 概述 本文介绍了 QtCreator mi…

AI编程常用工具 Jupyter Notebook

点击上方蓝色字体&#xff0c;选择“设为星标” 回复”云原生“获取基础架构实践 深度学习编程常用工具 我们先来看 4 个常用的编程工具&#xff1a;Sublime Text、Vim、Jupyter。虽然我介绍的是 Jupyter&#xff0c;但并不是要求你必须使用它&#xff0c;你也可以根据自己的喜…

PostgreSQL PG16 逻辑复制在STANDBY 上工作 (译)

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

Mybatis-plus从入门到精通

1、什么是MyBatis-Plus MyBatis-Plus&#xff08;简称MP&#xff09;是一个基于MyBatis的增强工具&#xff0c;在MyBatis的基础上对其进行扩展&#xff0c;用于简化MyBatis操作&#xff0c;提高开发效率。它继承了MyBatis原生的所有特性&#xff0c;并且添加了一些额外的功能&…

前端随笔:HTML/CSS/JavaScript和Vue

前端随笔 1&#xff1a;HTML、JavaScript和Vue 最近因为工作需要&#xff0c;需要接触一些前端的东西。之前虽然大体上了解过HTML、CSS和JavaScript&#xff0c;也知道HTML定义了内容、CSS定义了样式、JavaScript定义了行为&#xff0c;但是却没有详细的学习过前端三件套的细节…

微服务入门---SpringCloud(一)

微服务入门---SpringCloud&#xff08;一&#xff09; 1.认识微服务1.0.学习目标1.1.单体架构1.2.分布式架构1.3.微服务1.4.SpringCloud1.5.总结 2.服务拆分和远程调用2.1.服务拆分原则2.2.服务拆分示例2.2.1.导入Sql语句2.2.2.导入demo工程 2.3.实现远程调用案例2.3.1.案例需求…

CoTracker跟踪器 - CoTracker: It is Better to Track Together

论文地址&#xff1a;https://arxiv.org/pdf/2307.07635v1.pdf 官方地址&#xff1a;https://co-tracker.github.io/ github地址&#xff1a;https://github.com/facebookresearch/co-tracker/tree/main 引言 在计算机视觉领域&#xff0c;光流估计是历史最久远的问题之一&a…

利用VBA制作一个转盘游戏之五:最终的游戏过程

【分享成果&#xff0c;随喜正能量】真正厉害的人&#xff0c;从来不说难听的话&#xff0c;因为人心不需要听真话&#xff0c;只需要听好听的话&#xff0c;所以学着做一个有温度且睿智的人。不相为谋&#xff0c;但我照样能心平气和&#xff0c;冷眼相待&#xff0c;我依旧可…

BGP的介绍

目录 BGP基础 BGP的发展历史 BGP在企业中的应用 距离矢量型协议和路径矢量型协议的区别 BGP的特征 1.可控性 2.可靠性 3.AS-BY-AS BGP的对等关系 BGP的数据包 1.open报文 2.Keepalive报文 3.Update报文 4.Notification 报文 5.Route-refresh报文 BGP的状态机 …

【暑期每日一练】 day7

目录 选择题 &#xff08;1&#xff09; 解析&#xff1a; &#xff08;2&#xff09; 解析&#xff1a; &#xff08;3&#xff09; 解析&#xff1a; &#xff08;4&#xff09; 解析&#xff1a; &#xff08;5&#xff09; 解析&#xff1a; 编程题 题一…

【深度学习中常见的优化器总结】SGD+Adagrad+RMSprop+Adam优化算法总结及代码实现

文章目录 一、SGD&#xff0c;随机梯度下降1.1、算法详解1&#xff09;MBSGD&#xff08;Mini-batch Stochastic Gradient Descent&#xff09;2&#xff09;动量法&#xff1a;momentum3&#xff09;NAG(Nesterov accelerated gradient)4&#xff09;权重衰减项&#xff08;we…

使用 ChatGPT 进行研究的先进技术

在这篇文章中&#xff0c;您将探索改进您研究的先进技术。尤其&#xff0c; 分析和解释研究数据进行文献综述并找出研究差距废话不多说直接开始吧&#xff01;&#xff01;&#xff01; 分析和解释研究数据 一家小企业主希望分析客户满意度数据以改善客户服务。他们使用包含 10…

【Lua学习笔记】Lua入门

文章目录 Lua变量数据类型变量声明其他表示 Lua语法判断逻辑判断&#xff08;Lua很特殊&#xff0c;这个比较重要&#xff09;短路判断 ifif else 循环whileforrepeat 迭代器泛型for迭代器无状态迭代器多状态的迭代器 Lua函数select方法 数组字符索引_G &#xff08;不是教程&a…

opencv对相机进行畸变矫正,及矫正前后的坐标对应

文章目录 1.背景2.需求分析3.解决方案3.1.镜头畸变矫正3.2.知道矫正后的画面坐标(x, y)&#xff0c;求其在原画面的坐标(x, y)3.2.知道原画面坐标(x1, y1)&#xff0c;求其在矫正后的画面坐标(x2, y2) 4.效果5.代码 1.背景 目前有个项目&#xff0c;需要用到热成像相机。但是这…

k8s deployment(k8s经典版)|PetaExpress

Deployment是什么&#xff1f; Deployment是指在软件开发中将应用程序或系统部署到目标环境中的过程。它包括将代码编译、配置、打包并安装到目标服务器或设备上的步骤。k8s deployment是&#xff08;k8s经典版&#xff09;中用来管理发布的控制器&#xff0c;在开发的过程中使…