链表(link) 是一种长度几乎不受限制的数据结构。不同于列表,链表各个节点的内存位置不是连续的。
比如:
你的电脑内存被病毒占满了(真是一种神奇的病毒),只有3号内存、5号内存、6号内存、21号内存、92号内存为空。而你制作的杀毒软件必须占用4个内存空间(也是一种神奇的杀毒软件),如果你不知道链表,而把所有程序装在一个列表中,那么因为列表两个值的内存是连续的,所以,杀毒软件的安装无法成功(列表最长只能占2个内存,安装在5号内存和6号内存中)。这时,链表来了:
链表: 我可以成功安装杀毒软件。
你:怎么办?
链表:在我这里,各个节点的内存位置不是连续的。
你:你是怎么做到的?
链表:首先,我每一个节点都由两部分构成:数据域和指针域。前一个指针域指向下一个值。
你:哦,让我思考思考。
终于, 在链表的帮助下,你的电脑恢复了正常。
我相信,你一定很想知道你是怎么实现的链表,修复你的电脑——你对刚才发生的事情一无所知(画外音:那当然,刚才的事是你编的。)。因为,在python中,并不存在指针类型。
我们知道,链表的各个节点的内存位置不是连续的,每个节点由数据域和指针域构成,所以,在__init__
函数中,我们定义两部分
class Link: # 创建一个link类
def __init__(self, data=None, p=None):
self.data = data # 添加数据域
self.p = p # 添加指针域
你说:那我依然不认为这个链表各个节点的内存位置不是连续的。别急,慢慢来。
在这之后,我需要用__str__
函数打印出整个链表,那么,我直接让一个节点的指针域等于下一个节点,再通过一点点递归算法,打印出链表。
def __str__(self): # 返回整个链表
return '{} -> {}'.format(self.data, self.p)
重头戏来了,许多人问:“作者,什么叫:‘我直接让一个节点的指针域等于下一个节点’啊?我觉得指针域就是None呀。”
作者:格局小了。
我们先来看,如何在链表尾部添加一个值。按照我“直接让一个节点的指针域等于下一个节点”的思路,添加值时,我们只要把最尾部的节点的指针域改为需要连接的链表。
至于如何找到最尾部的节点,我们用一个小小的递归……
def __add__(self, other):
tail = self.p
if not tail: # 链表节点的指针域为空
self.p = other # 把最尾部的节点的指针域改为用于连接的链表
else:
tail.__add__(other) # 递归
在链表的操作中,我们当然不能只在尾部插入一个值,不然就快成栈了。那么,insert
函数出现了。
首先,通过for循环找到插入链表位置的节点,然后,把用于插入节点的指针域设为插入链表位置节点的指针域,再把插入链表位置节点的指针域设为用于的插入节点
def insert(self, other, index):
try: # 尝试索引,报错时抛出索引错误
value = self
for cnt in range(index):
value = value.p
other.p = value.p
value.p = other
except AttributeError:
raise IndexError('link index out of range')
此时,许多人的电脑都感染上的病毒。只可惜,你把杀毒软件的源文件删了,你必须从电脑中找到杀毒软件的第二个值,那是安装程序,那么,怎么办?
你:凉拌。
……
首先,这个好办。我们知道,链表节点由数据域和指针域构成。那么,先通过一个for循环,找到第二个节点
def serch(self, index):
value = self
for cnt in range(index):
value = value.p #找到下一个节点
然后返回该节点的数据域
return value.data
有一种可能:如果索引超出链表长度。这时,因为最后一个节点的指针域不在存在指针域,会抛出AttributeError
错误。所以,套上一个try语句并抛出IndexError
错误。··
def serch(self, index):
try:
value = self
for cnt in range(index):
value = value.p
return value.data
except AttributeError:
raise IndexError('link index out of range')
终于,其他人的电脑也恢复了正常。
病毒又出现了。这次,如果不管它,病毒会在48小时内消失,如果使用了杀毒软件,病毒会在5分钟内消失。但如果内存被占满了,电脑就崩了。
然后,Tom的电脑也中了病毒,只有三个内存为空,此时,假如安装软件照常安装杀毒软件,他的电脑必死无疑。当然,病毒不会全占满电脑内存。
此时,你察觉到,必须对剩余内存和链表长度进行比较。
我们需要检查链表长度,用__len__
函数即可。先得到该链表的__str__
值,再用split()
函数分开每一项,通过len()
返回长度(因为包括了尾节点的指针域——None,需要返回长度减1)。
def __len__(self):
return len(str(self).split(' -> ')) - 1
可恶至极,病毒又来了。这次,它会抓住杀毒软件第三个值的漏洞,删除杀毒软件。你必须删掉链表第三项。
你:我不干啦!!!!!!!!!!!
非常简单,删除某个节点时,我们只要把它前一个节点的指针域指向被删除节点的指针域。
def remove(self, index):
try:
value = self
for cnt in range(index - 1):
value = value.p
value.p = value.p.p
except AttributeError:
raise IndexError('link index out of range')
终于,你制作了一个看似完美的杀毒软件
店小二:完了,病毒把所有内存都占满了!!!!
下载代码源文件
class Link: # 创建一个link类
def __init__(self, data=None, p=None):
self.data = data # 添加数据域
self.p = p # 添加指针域
def __str__(self): # 返回整个链表
return '{} -> {}'.format(self.data, self.p)
def __len__(self):
return len(str(self).split(' -> ')) - 1
def __add__(self, other):
tail = self.p
if not tail # 链表节点的指针域为空
self.p = other # 把最尾部的节点的指针域改为用于连接的链表
else:
tail.__add__(other) # 递归
def insert(self, other, index):
try:
value = self
for cnt in range(index):
value = value.p
other.p = value.p
value.p = other
except AttributeError:
raise IndexError('link index out of range')
def serch(self, index):
try:
value = self
for cnt in range(index):
value = value.p
return value.data
except AttributeError:
raise IndexError('link index out of range')
def remove(self, index):
try:
value = self
for cnt in range(index - 1):
value = value.p
value.p = value.p.p
except AttributeError:
raise IndexError('link index out of range')
本文为原创,连载须注明出处。
(本故事情节虚构,如有雷同,纯属巧合)