用单向链表实现元素进栈大概分为以下几个步骤;
-
创建节点
-
用链表的头部插入/删除元素
-
元素进栈
1.创建节点
_Node 类的构造函数是为了方便而设计,它允许为每个新创建的节点赋值。
由于 python 没有指针,因此一般使用类的结构实现指向关系。
例如:
class TreeNode:
# 类的结构包含了指向关系
def __init__(self,x):
self.val = x
self.next = None
# 创建实例
l1 = TreeNode(0)
l2 = TreeNode(1)
l3 = TreeNode(2)
# 引用表示指向关系
l1.next = l2
l2.next = l3
运行结果如下:
In [1]:l3
Out[1]: <__main__.TreeNode at 0x24d3e274908>
In [2]:l1.next.next
Out[2]: <__main__.TreeNode at 0x24d3e274908>
In [3]:l3.val
Out[3]: 2
In [4]:l1.next.next.val
Out[4]: 2
可以看出 l1.next.next 和 l3 指向同一个地址。
用单向链表实现元素压栈中,创建的节点为轻量级节点 _Node,为此定义了一个 __slots__对象,这是因为 python 中的命名空间均代表内置 dict 类的一个实例,即将范围内识别的名称与相关联的对象映射起来,这会导致额外内存使用量。
因此,这里使用__slots__声明,将所有实例成员分配给一个固定的字符串序列,利用一组元组来自动打包。
class _Node:
# 赋值右边是一组元组
__slot__='_element','_next'
创建节点的代码如下:
# 创建节点
class _Node:
__slot__='_element','_next'
def __init__(self,element,next):
self._element = element
self._next = next
2.在链表的头部插入/删除元素
只有在链表头部才能实现常熟时间内的有效插入和删除元素。为避免每次返回栈的大小时,必须遍历整个列表,因此定义一个变量 _size 持续追踪当前元素的数量。
def __init__(self):
self._head = None
self._size = 0
3.元素压栈
当栈顶插入新元素时,调用 _Node 类来完成链接结构的改变。
def push(self,e):
self._head = self._Node(e,self._head)
self._size += 1
每一个新元素进栈,将其赋予 _Node 类创建一个新节点,新节点的 _next 指针被设置为前一个栈顶的元素,头指针设置为新节点。即原栈顶后移,新元素成为栈顶。
这里可以创建一个实例更直观地了解一下。
#创建ls实例
ls=LinkedStack()
ls.push(1)
ls.push(2)
In [1]: ls._head._element
Out[1]: 2
In [2]:ls._head._next._element
Out[2]: 1
当第一次调用 ls.push(1) 的时候,栈顶为 1,当第二次调用 ls.push(2) 的时候, self._Node(e,self._head) 中 self._head 为 _element 为 1 的元素,调用 _Node 类的 self._next = next ,self._head 设置为 _next,实现原栈顶后移,同时 _head 设置为 2 的元素,新元素成为栈顶。
完整代码如下
class LinkedStack:
# 创建节点
class _Node:
__slot__='_element','_next'
def __init__(self,element,next):
self._element = element
self._next = next
# 设置栈顶
def __init__(self):
self._head =None
self._size = 0
# 元素压栈
def push(self,e):
self._head = self._Node(e,self._head)
self._size += 1
ls=LinkedStack()
ls.push(1)
ls.push(2)