栈==python的两种实现方式==通俗易懂

栈(python的两种实现方式)

引言:线性表

线性表:由零个或者多个数据元素组成的有限的序列(有头有尾),常见的线性表有顺序存储结构(顺序表)或者链式存储结构(链表)

  • 一般线性表:可以自由操作结点,如链表、顺序表
  • 受限线性表:对节点的操作受到限制,如栈、堆

栈属于受限线性表,相较于普通的线性表(链表,顺序表)而言,删掉了好多的功能,其仅仅只可以对一个数据结点进行操作,这样子做是为什么?

​ 简单来讲,就是有的时候没有必要,用不到的功能,没有必要让程序跑一遍,杀鸡用牛刀!

​ 在能够完成需求的情况下,代码越简洁越好,特殊情况除外(大伙自己体会嘿嘿,可以交流!)。


栈====通俗解释呦

***栈(通俗解释)***:一个细长的小房子,不是小盒子呦,房子里可以装小盒子,但是有一个要求,第一个盒子必须放到房子的最里面,第二个盒子紧贴着第一个盒子,并盖住第一个盒子,以此类推…

使得如果想要拿盒子的时候,只能够拿最外面的盒子,里面的盒子均不能够操作,盒子里装的就是数据元素,房子就是栈。

  • 最外面的盒子叫做栈顶元素,第一个盒子叫做栈底元素
  • 遵循后进先出

如下示意图:
在这里插入图片描述

官方图示如下:
在这里插入图片描述


python代码两种方式进行实现===栈

栈实现的操作

栈属于受限线性表

线性表有两种模式顺序存储结构链式存储结构

接下来我们两种均进行实现
在这里插入图片描述


用顺序表实现栈(附件中含完整代码)

本质还是用列表(顺序表),往下看吧先生嘿嘿。

建立框架

注意:框架中的data采用私有属性,使得栈无法在外部对其进行访问,也就满足了,除了最后一个箱子其他箱子均无法对其进行操作。

'''
用pyhton实现栈====顺序表
'''
class Stack():
    def __init__(self):
        self.__data = []    # 用顺序表进行实现
        '''
        两个下划线代表私有属性
        '''

    def push(self,data):
        # 添加一个元素到栈顶
        pass

    def pop(self):
        # 弹出栈顶元素,并返回栈顶元素
        pass

    def top(self):
        # 返回栈顶元素
        pass

    def is_empty(self):
        # 判断栈是否为空
        pass

    def size(self):
        # 返回栈的元素个数
        pass

添加一个元素到栈顶

  1. 对于push方法,我们先来思考这样一个问题,这个方法可以用列表中的什么方法进行实现,哪种更好?

    两种方法:

    • append()直接在栈的尾部添加一个元素
    • insert()添加元素在任意位置
  2. 那么我们将栈顶元素放到列表的尾部好,还是列表的头部好?

    放到头部=======时间复杂度 O(n)因为所有的元素都会向后移一位

    放到尾部=======方法时间复杂度O(1)

    故结果不用我多说了吧,栈顶元素放在尾部

代码实现:

    def push(self,item):
        # 添加一个元素到栈顶
        self.__data.append(item)

判断栈是否为空

 def is_empty(self):
        # 判断栈是否为空
        return self.__data == []

弹出栈顶元素

将栈顶元素(小盒子)从小房子中扔出去,并且输出该小盒子

我们运用列表中的pop方法

  • 列表的pop方法,直接删除列表中指定索引的位置的元素,并返回该元素的值
    当不指定参数 的时候 默认删除最后一个元素
  • 但是当参数不存在的时候或者列表为空的时候会报错,这个时候要加入报错语句

代码实现:

    def pop(self):
        if self.is_empty():
            raise ValueError('栈为空') 
            '''当if中的条件成立的时候  则return self.__data.pop(-1)一定会出错这个时候我们需要进行手动抛出异常,那么这个方法就不会继续向下执行了,会及时停止'''
        return self.__data.pop(-1)

返回栈顶元素

返回列表中的最后一个元素即可

    def top(self):
        # 返回栈顶元素
        return self.__data[-1]

返回栈的元素个数

返回列表长度即可

    def size(self):
        # 返回栈的元素个数
        return len(self.__data)

用链表现栈(附件中含完整代码)

建立框架

在这里我们需要思考一个问题,将链表的头节点作为栈顶元素还是将链表的尾结点作为栈顶元素?

如果将头节点作为栈顶元素时间复杂度位O(1)

如果将尾结点作为栈顶元素时间复杂度位O(n)会遍历所有的链表结点

故:将头节点作为栈顶元素

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

class Stack():
    def __init__(self):
        self.top = Node   # 将头节点作为栈顶元素
        self.size = 0     # 栈的元素个数

添加一个元素到栈顶

正常情况下:

  1. 创建一个新的结点node
  2. node的下一节点指向原来的头结点
  3. 将头节点指向node
  4. 长度加1

注意:以下涉及到了代码的融合

    def push(self,item):
        # 添加一个元素到栈顶
        node = Node(item)
        node.next = self.top
        self.top = node
        self.size += 1

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

class Stack():
    def __init__(self):
        self.top = Node   # 将头节点作为栈顶元素
        self.size = 0     # 栈的元素个数

    def push(self,item):
        self.top = None(item,self.top)   # 精髓代码
        self.size += 1

精髓代码解析:
在这里插入图片描述


弹出栈顶元素

正常情况思路

  1. 将栈顶元素的data属性赋值给value变量
  2. 将头节点指向栈顶元素的next属性
  3. 长度减1
  4. 返回value

特殊的:

当为空栈的时候能不能运行?
显然是不行的,因为空栈的时候,栈顶元素为None没有data属性

故代码如下:

    def pop(self):
        # 弹出栈顶元素
        if self.is_empth():
            raise ValueError('栈为空')
        value = self.top.data
        self.top = self.top.next
        self.size -= 1
        return value

返回栈顶元素

返回头节点的data属性即可

但是要考虑当栈为空的时候,头节点为None,没有data属性。

    def top(self):
        if self.is_empth():
            raise ValueError('栈为空')
        return self.top.data

raise代码讲解:
i f   s e l f . i s _ e m p t h ( ) :                                       r a i s e   V a l u e E r r o r ( ′ 栈 为 空 ′ ) if \ self.is\_empth():\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ \ \ \ \ \ \ \ \ \ raise\ ValueError('栈为空') if self.is_empth():                                    raise ValueError()
以上代码的含义为,主动判断错误,我们已知当self.is_empty()==True的时候下面代码运行的时候会出现错误,呢么我们干脆直接在上面就主动 抛出一个异常,让内部程序停止执行。但是主程序还是会继续运行的!


返回栈长度和是否为空

长度===>size属性就行

是否为空===>size属性是否为0

    def is_empth(self):
        return not self._size

    def size(self):
        return self._size

一个小小的代码可能犯的错误

在这里插入图片描述

但是在这里值得注意的是上述代码return self._size中的下划线不能不写,因为函数名和方法名一旦相同的话,在调用属性(和方法同名)的时候,默认会引用函数!

纠正方法:方法名和属性名不可相同

错误代码
    def size(self):
        return self.size

return self.size代码的意思就是在size方法(函数)的内部引用这个函数

if __name__ == '__main__':
    stack = Stack()
    stack.push(1)
    stack.push(2)
    print('这是对象的size属性')
    print(stack.size) 
    print'这是对象的size方法'print(stack.size()) 

执行我们会得到以下运行结果
在这里插入图片描述

这段代码中同时使用相同的名称 self.size 作为属性名和方法名会导致错误。这是因为在调用 self.size 时,解释器会将其理解为方法而不是属性。

改正后的代码
    def size(self):
        return self._size
if __name__ == '__main__':
    stack = Stack()
    stack.push(1)
    stack.push(2)
    stack.push(3)
    stack.push(4)
    stack.push(5)
    print(stack.is_empth())
    print(stack.size())     # 调用size方法(在类里面定义的def叫方法,在类外面定义的叫函数)
    print(stack._size)      #调用size属性
    print(stack.size)       # 引用size方法

运行结果如图

在这里插入图片描述

代码详解:

  • print(stack.size())调用size方法,方法里面调用_size属性,并返回
  • print(stack._size)调用\_size属性
  • print(stack.size)引用size方法,只是引用了方法本身,并没有实际调用该方法。它可以用于获取方法对象或将方法作为参数传递给其他函数/方法。

附件1:python解释器执行优先级

解释器在解析代码时会按照一定的优先级顺序进行处理。下面是一般情况下解释器的处理顺序:

  1. 当在函数体内部调用一个变量时,解释器会首先查找当前作用域内是否有该变量的局部定义。
  2. 如果当前作用域内没有找到相应的局部定义,解释器会继续查找包围当前作用域的上层作用域,包括嵌套的函数或类的外部作用域。
  3. 如果在上层作用域中找到了相应的变量定义,解释器会使用该定义。
  4. 如果在任何一个作用域中都没有找到相应的变量定义,解释器会抛出 NameError 异常。

在上述报错代码中,当函数体内部调用 self.size 时,解释器会将其理解为函数调用(方法调用),而不是属性访问。因此,在函数体内部使用 self.size 时,解释器会查找函数所属的对象是否有名为 size 的方法,而不是属性。


附件2:完整代码

顺序表完整代码

# @Author  :泰敢人辣
# Date     :2023-07-27 21:49

'''
用pyhton实现栈====顺序表
'''
class Stack():
    def __init__(self):
        self.__data = []    # 用顺序表进行实现

    def push(self,item):
        # 添加一个元素到栈顶
        self.__data.append(item)

    def pop(self):
        if self.is_empty():
            raise ValueError('栈为空')  # 当if中的条件成立的时候  则return self.__data.pop(-1)一定会出错这个时候我们需要进行手动抛出异常,那么这个方法就不会继续向下执行了,会及时停止
        return self.__data.pop(-1)


    def top(self):
        # 返回栈顶元素
        return self.__data[-1]

    def is_empty(self):
        # 判断栈是否为空
        return self.__data == []

    def size(self):
        # 返回栈的元素个数
        return len(self.__data)


if __name__ == '__main__':
    stack = Stack()
    stack.push(1)
    stack.push(1)
    stack.push(1)
    stack.push(4)
    print(stack.size())
    print(stack.pop())

链表完整代码

# @Author  :泰敢人辣
# Date     :2023-07-28 0:03


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

class Stack():
    def __init__(self):
        self.top = Node   # 将头节点作为栈顶元素
        self._size = 0     # 栈的元素个数

    def push(self,item):
        # 添加一个元素到栈顶
        '''
        正常情况下:
        1.创建一个新的结点node
        2.node的下一节点指向原来的头结点
        3.将头节点指向node
        4.长度加1
        如果是空链表是否可以执行?
        思考后==>可以执行
        :param item:
        :return:
        '''
        node = Node(item)
        node.next = self.top
        self.top = node
        self._size += 1

        '''    
        def push(self,item):
        self.top = None(item,self.top)
        self.size += 1'''

    def pop(self):
        # 弹出栈顶元素
        if self.is_empth():
            raise ValueError('栈为空')
        '''
        1. 将栈顶元素的data属性赋值给value变量
        2. 将头节点指向栈顶元素的next属性
        3. 长度减1
        4. 返回value
        '''
        value = self.top.data
        self.top = self.top.next
        self._size -= 1
        return value

    def top(self):
        if self.is_empth():
            raise ValueError('栈为空')
        return self.top.data


    def is_empth(self):
        return not self._size

    def size(self):
        return self._size

if __name__ == '__main__':
    stack = Stack()
    stack.push(1)
    stack.push(2)
    stack.push(3)
    stack.push(4)
    stack.push(5)
    print(stack.is_empth())
    print(stack.size())     # 调用size方法(在类里面定义的def叫方法,在类外面定义的叫函数)
    print(stack._size)      #调用size属性
    print(stack.size)       # 引用size方法
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

TAGRENLA

您的打赏是我创作的动力,谢谢!

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

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

打赏作者

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

抵扣说明:

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

余额充值