栈(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
添加一个元素到栈顶
-
对于push方法,我们先来思考这样一个问题,这个方法可以用列表中的什么方法进行实现,哪种更好?
两种方法:
- append()直接在栈的尾部添加一个元素
- insert()添加元素在任意位置
-
那么我们将栈顶元素放到列表的尾部好,还是列表的头部好?
放到头部=======时间复杂度 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 # 栈的元素个数
添加一个元素到栈顶
正常情况下:
- 创建一个新的结点node
- node的下一节点指向原来的头结点
- 将头节点指向node
- 长度加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
精髓代码解析:
弹出栈顶元素
正常情况思路
- 将栈顶元素的data属性赋值给value变量
- 将头节点指向栈顶元素的next属性
- 长度减1
- 返回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解释器执行优先级
解释器在解析代码时会按照一定的优先级顺序进行处理。下面是一般情况下解释器的处理顺序:
- 当在函数体内部调用一个变量时,解释器会首先查找当前作用域内是否有该变量的局部定义。
- 如果当前作用域内没有找到相应的局部定义,解释器会继续查找包围当前作用域的上层作用域,包括嵌套的函数或类的外部作用域。
- 如果在上层作用域中找到了相应的变量定义,解释器会使用该定义。
- 如果在任何一个作用域中都没有找到相应的变量定义,解释器会抛出
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方法