python数据结构---2.线性数据结构(栈)

前言:

在这个系列的第一篇博客中,我主要强调了编程思维的重要性以及如何高效准确的编写出能解决问题的代码,这一篇博客开始就正式开始python数据结构相关的知识。

有一种数据结构,它的元素顺序取决于添加的顺序或者删除的顺序,一旦某个元素被添加进来,它与前后元素的相对位置也就保持不变了,这样的数据集合就被称为线性数据结构

常见的线性数据结构主要有:栈、队列、双端队列、列表。接下来的几篇博客会来详细讲讲这几种线性数据结构和它们的实现

今天的主角:栈

,它的添加和删除操作总发生在同一端,即顶端。它的特性呢就是“先进后出”(或者说“后进先出”,都是一个意思),也就是说最先进入栈的元素在栈中会呆最久,那么顶端的元素总是最近才添加的,而底端的元素总是最开始添加的。

生活中有很多栈的例子,比如:我们洗好的盘子叠起来放,总是先洗好的放在最下面;一堆摞起来的书,我们要拿到其中某一本,就需要先移开上面的那些书;或者有没有想过,我们浏览网页之所以可以返回之前浏览的网页,那是不是也是因为我们浏览的网页记录被放到栈中了呢?

栈抽象数据类型的操作

  • stack() 初始化一个空栈,返回值一个空栈
  • push(item) 入栈操作,将一个元素item放入栈中
  • pop() 出栈操作,将栈顶元素删除,并且返回栈顶元素的值
  • peek() 查看栈顶元素,与pop不同,它不会删除栈顶元素,只是将栈顶元素的值返回
  • isEmpty() 检查栈是否为空
  • size() 返回栈中的元素数量
  • show() 显示栈中所有元素

有了这些基本操作,那我们就尝试着实现一个栈

1.栈的python实现

class Stack():
    def __init__(self):
        self.items=[]
    
    def isEmpty(self):
        return '栈为空'if self.items==[] else '栈不为空'
    
    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)
    
    def show(self):
        return self.items
    
# 实例化测试一下
s=Stack()

# 初始应该为空
print(s.isEmpty())

# 压入几个元素试试
s.push(4)
s.push('abc')
s.push(18)
s.push('DL')

# 再来看看是否为空,以及栈的大小和栈顶元素
print(s.isEmpty())
print(s.size())
print(s.peek())

# 然后试试删除栈顶元素
print(s.pop())
print(s.size())

# 显示所有元素
print(s.show())

在这里插入图片描述
通过上面的代码,我们可以很好的用代码实现栈的各种操作。同时大家肯定也注意到了,我说过python写数据结构比c、c++简单,原因就在于python的列表是动态的,没有固定的大小,有元素添加直接append就可以啦,相比于当时学习c的数据结构,一开始就要分配数组大小,然后添加元素还得判断栈是否满了,省去了大量的精力。不过,也许这样确实简单,但是我们还是需要去考虑python的动态列表是怎么实现的呢?或者有兴趣也可以去看看go语言切片的实现原理,了解一下长度和容量是如何变化的,为什么推荐go,因为go更类似于C语言,但是会比C语言简单,在这里我就不拓展太多了,还是本着最低学习成本的思想来讲

思考

如果我们用列表头作为栈顶,如何实现栈?与上面用列表尾实现效率上有什么不同

简单解释:用列表头作为栈,入栈和出栈都是对items[0]进行操作,入栈用insert(0,item),出栈用pop(0);从效率上来说,insert(0),pop(0)时间复杂度是O(n),而append(),pop()时间复杂度是O(1),所以栈中元素越多,列表尾作为栈顶的优势越明显。

2.栈的应用

今天先来说说常见的两个应用:匹配符号,表达式转换

  1. 我们知道(),[],{}总是成对出现,并且有一定的顺序,所以我们可以利用栈实现一个检测器,看输入的符号是否符合规范。

  2. 对于表达式,我们有中序表达式也有前序,后序,它们之间的转换也可以利用栈实现

思路:

  1. 我们遍历输入的表达式,将([{ 压入栈,然后一旦遍历到右边的符号就去做一次判断:首先把栈顶元素出栈,然后与它现在右边的符号做一次匹配,看是否符合规范;依次这样,如果最后栈为空(全部左边符号做过匹配),而且每一次匹配的结果(flag)都是正确的,那么也就是符号匹配正确。

  2. 以中序转后续为例,我们先遍历表达式,用栈保存运算符,并且每次遇到新的运算符都要先比较它与栈中元素的优先级,从而实现转换

这里呢,我们会使用pythonds这个库里面的数据结构,需要先pip install pythonds

from pythonds.basic import  Stack

def check(string):
    s=Stack()
    flag=True
    index=0
    
    while(index<len(string) and flag):
        symbol=string[index]
        if symbol in "([{":
            s.push(symbol)
        else:
            if s.isEmpty():
                flag=False
            else:
                top=s.pop()
                if not matches(top,symbol):
                    flag=False
        index+=1
        
    if flag and s.isEmpty():
        return "符号匹配无错误"
    else:
        return "符号匹配有错误"
        
def matches(start,end):
    starts='([{'
    ends=')]}'
    return  starts.index(start)==ends.index(end)


print(check('(][){}'))
print(check('([{}])'))
print(check('[][][]'))
print(check('[{())}]'))

在这里插入图片描述

from pythonds.basic import Stack
import string

def infix_to_postfix(expr):
    #先定义一下运算符的优先级
    prec={}
    prec['*']=3
    prec['/']=3
    prec['+']=2
    prec['-']=2
    prec['(']=1
    
    op=Stack()
    postfix_list=[]
    
    # 以表达式中的空格分开,得到表达式中的每一个元素,返回列表
    tokenlist=expr.split()
    # 开始遍历
    for token in tokenlist:
        # 如果匹配到大写的英文字母直接添加到后续表达式
        if token in string.ascii_uppercase:
            postfix_list.append(token)
        # 如果匹配到左括号,那就直接入栈
        elif token=='(':
            op.push(token)
        # 匹配到右括号,就把栈中左括号之后添加的所有符号都拿出来放进后续表达式中
        elif token==')':
            topToken=op.pop()
            while topToken!='(':
                postfix_list.append(topToken)
                topToken=op.pop()
        # 栈不为空,且当前符号优先级小于栈顶元素优先级,
        # 那么就把栈顶元素添加到后序表达式,同时把优先级小的运算符入栈
        else:
            while (not op.isEmpty()) and (prec[op.peek()]>=prec[token]):
                postfix_list.append(op.pop())
            op.push(token)
    
    while not op.isEmpty():
        postfix_list.append(op.pop())
    return " ".join(postfix_list)

print(infix_to_postfix("( A + B ) * ( C + D )"))
print(infix_to_postfix("A + B * C"))
print(infix_to_postfix("A * B + C"))

在这里插入图片描述

总结

这篇博客,了解了栈的特点以及基本操作,然后还知道了可以利用栈去解决实现哪些问题,下一篇就讲讲队列。这个系列的博客其实每篇内容都不多,也是为了一点一点的能更好吸收。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在 Python 中,有几种常见的非线性数据结构: 1. 字典(dict):字典是一种映射类型,它将键映射到值。字典是无序的,并且没有下标。你可以使用键来访问字典中的元素。 2. 集合(set):集合是一种无序的、不重复的元素的集合。集合没有下标,也不能使用下标来访问元素。 3. 队列(Queue):队列是一种先进先出(FIFO)的数据结构。 4. (Stack):是一种后进先出(LIFO)的数据结构。 5. 图(Graph):图是一种非线性数据结构,由节点和边组成。图中的节点表示数据,边表示两个节点之间的关系。 这些数据结构都可以在 Python 中使用,你可以根据自己的需要选择适合的数据结构。 ### 回答2: Python中的非线性数据结构包括列表、元组、字典和集合。 首先,列表(List)是Python中常用的非线性数据结构之一。列表是由一系列元素组成的有序集合,可以包含不同类型的元素。列表可以通过索引来访问和修改元素,还支持添加、删除和排序等操作,非常灵活。 其次,元组(Tuple)也是一种非线性数据结构。与列表类似,元组是有序的,但是不可修改的。元组通常用于存储不同类型的数据,比如一个人的姓名、年龄和性别等信息。元组的元素可以通过索引来访问。 另外,字典(Dictionary)是Python中非常强大的非线性数据结构。字典是由一系列键-值对组成的无序集合,每个键都唯一且不可变。字典可以通过键来访问和修改对应的值,也支持添加、删除和查找等操作。字典的应用非常广泛,比如存储学生的成绩、员工的工资等信息。 最后,集合(Set)是一种无序且不重复的数据结构。集合中的元素是独一无二的,不支持通过索引访问。集合可以用来去除重复的元素,还支持并、交、差等常见的集合运算。 总而言之,Python中的非线性数据结构包括列表、元组、字典和集合,它们提供了丰富的操作和功能,可以灵活地处理各种数据。 ### 回答3: Python中的非线性数据结构是指那些不是按照线性顺序排列的数据结构。这些数据结构不仅能够存储单个数据元素,还能够通过引用来连接多个数据元素,从而形成更复杂的数据组织形式。 在Python中,常见的非线性数据结构有以下几种: 1. 列表(List):列表是Python中最常用的数据结构之一,它可以存储任意类型的数据,并且数据元素之间可以具有不同的顺序。列表是一种有序、可变的数据结构,可以通过索引来访问、修改和删除列表中的元素。 2. 元组(Tuple):元组类似于列表,但是它是一种不可变的数据结构,即元组中的元素不能被修改。元组通常用于存储相关的数据项,例如存储一个人的姓名、年龄和性别等信息。 3. 字典(Dictionary):字典是一种键值对(key-value)的数据结构,在字典中,每个元素都由一个键和一个值组成。字典中的元素是无序的,但是通过键可以快速访问、修改和删除对应的值。 4. 集合(Set):集合是一种无序的、唯一的数据结构,它可以存储任意类型的数据,但是不允许重复的元素。集合可以进行并集、交集、差集等常见的集合操作,对于处理数学集合关系非常方便。 Python中的非线性数据结构具有灵活性和易用性,可以满足不同类型的数据组织需求。通过合理地使用这些数据结构,我们可以更高效地处理和存储数据,并且简化程序的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shelgi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值