链接表把每个元素储存在一批独立的储存块里,在每个节点中显式的保存下一个节点的链接,从每个节点都可以找到与其相关的下一个节点。
单向链接表
节点为一个二元组,分别保存着元素数据和下一个节点的链接,在尾节点的链接域设置一个空链接来表示表的结束。
定义一个简单的表节点类:
class LNode:
def __init__(self, elem, next_=None):
self.elem = elem
self.next = next_
链表基本操作
- 创建空链表:
只需要把相应的表头变量设置为空链接:None,就代表没有后续元素,即为一个空表。O(1) - 删除链表:
应丢弃这个表中的所有节点,在Python中只需把表的指针赋值为None,就抛弃了所有的节点,Python的存储管理系统会自动回收内存。O(1) - 判断表是否为空:
检查表头的链接是否为空。O(1)
加入元素:
链接表加入元素不需要移动已有的数据,只要通过修改链接把新节点加入到合适的位置就行;这里有两种情况:
- 首端插入
创建一个新节点,把原表首节点连接存入到新链接的链接域(原表首节点现在在新节点之后),修改表头变量,指向新节点,新节点称为表的开始。O(1)
q = LNode(13)
q.next = head.next
head = q
- 一般情况下插入
找到要插入位置之前的那个点,将它链接域的值复制到新节点的链接域,然后将自己的链接域改为新节点的链接。O(n)
q =LNode(13)
q.next = pre.next
pre.next = q
删除元素:
- 删除表首元素,将表头指针指向第二个节点。O(1)
head = head.next
- 一般情况下元素删除,找到要删除元素的前一个节点,修改链接域,指向下一个节点。O(n)
pre.next = pre.next.next
扫描、定位和遍历
单链表只有一个方向,只知道一个表头变量,所以对表的检查都是从表头变量开始,沿着表中链接逐步进行,这种操作称为扫描,基本操作模式为:
p = head
while p is not None and 其他条件:
对p节点里的数据操作
p = p.next
变量p称为扫描指针,每次迭代前检查
- 按下标定位O(n)
p = head
while p is not None and i>0:
i -=1
p = p.next
- 按元素定位O(n)
假设需要满足函数pred():
p = head
while p is not None and not pred(p.elem):
p = p.next
- 遍历O(n)
p = head
while p is not None:
print p.elem
p = p.next
求表的长度O(n)
def length(head):
p, n = head, 0
while p is not None:
n += 1
p = p.next
return n
单链表类的实现
- 节点类的使用
class LNode:
def __init__(self, elem, next_=None):
self.elem = elem
self.next = next_
llistl = LNode(1) #实例化一个节点
p = llistl #p指向第一个节点
for i in range(2, 11):
p.next = LNode(i) #第一个节点的链接域指向下一个实例化的节点
p = p.next #将p指向下一个节点,然后循环
p = llistl
while p is not None:
print p.elem
p = p.next
输出:
1
2
3
4
5
6
7
8
9
10
- 自定义异常
class LinkedListUnderflow(ValueError):
pass
- LList类的定义
class LList():
def __init__(self):
self._head = None
def is_empty(self):
return self._head is None
def prepend(self, elem): #在首部添加元素
self._head = LNode(elem, self._head) #把新元素的链接域设为原来_head的值,_head指向新元素,为第一个元素,新元素又指向旧元素
def pop(self): #删除首个元素并返回它的值
if self._head is None: #无节点引发异常
raise LinkedListUnderflow("in pop")
e = self._head.elem #获取首节点的元素
self._head = self._head.next #_head指向下一个元素
return e
def append(self, elem): #在表后加入元素,两种情况
if self._head is None: #空链表加入方法
self._head = LNode(elem)
return
p = self._head #从头检查,直到检查到最后一个元素,在它的next里加入新元素的链接
while p.next is not None:
p = p.next
p.next = LNode(elem)
def pop_last(self): #删除最后元素并返回它的值
if self._head is None: #为空表时引发异常
raise LinkedListUnderflow("in pop_last")
if self._head.next is None: #只有一个元素的情况
e = self._head.elem
self._head = None
return e
p = self._head #p.next.next为None的元素为倒数第二个元素
while p.next.next is not None:
p = p.next
e = p.next.elem
p.next = None
return e
def find(self, pred):
p = self._head
while p is not None:
if pred(p.elem):
return p.elem
p = p.next
def printall(self): #打印表中元素
p = self._head
while p is not None:
print p.elem, end=''
if p.next is not None:
print ',', end=''
p = p.next
print 'END'
def for_each(self, proc): #对每个元素进行谓语函数的操作
p = self._head
while p is not None:
proc(p.elem)
p = p.next
#为了使LList可迭代,最简单的方式是定义生成器函数。
def elements(self):
p = self._head
while p is not None:
yield p.elem
p = p.next
#这样LList.elements()为一个生成器,就可以用for来迭代LList.elements()了
#把生成器和上边的find方法结合就可以做成一个筛选器
def filter(self, pred):
p = self._head
while p is not None:
if pred(p.elem):
yield p.elem
p = p.next