理解数据结构最好的方式就是用脑洞把它想象出来。
一、节点
class Node():
def __init__(self,data=None):
self.data=data
self.left=None
self.right=Nonenode = Node(5)
现在请你闭眼在脑海创造一片虚无缥缈的空间,空间里产生一个盒子,盒子携带了三个属性,其中一个数据携带了数据5,另外两个属性暂时是空的。没错,这就是一个节点,它是链表的基础单位,一会我们将用创造很多这样的盒子组合出很多强大的功能。现在先释放掉你的想象,让盒子湮灭于时空中,一会用到的时候我们可以随时创造。
二、链表类
对于一个列表对象,我们可以直接使用L.append()添加数据,十分方便。我们同样可以创造一个链表类,假设为Link,我们同样可以使用link.function()这样的形式去实现一个功能。唯一不同点,列表是Python内置的类,链表是我们将要凭空创造的类。下面我们创造一个链表类。
class link():
def __init__(self):
self.root=None
如你所见,这个链表类除了self.root指向了一个None,还不能做任何事。我们先来创建一个实例Link=link()。你可以把这个实例想象为一个绿点,当然你也可以把它想象成绿色的长方形,随你喜欢。下面我们为链表类添加一些方法。
三、增加元素
你脑子里的小宇宙中应该有一个红色的点,孤零零的飘荡。我们再创造一个节点,就是前面提到的盒子,盒子的数据属性(self.data)为5。现在让红点的self.root指向这个节点。链表对象用一个箭头束缚了这个节点,它们俩愉快的被这条线绑起来了。
代码是这样的。
def addhead(self,data):
node=Node(data)
self.root=node
现在我们的Link对象拥有一个节点了。我们可以通过Link.root.data访问到数据5。我们继续尝试添加第二个节点,你会发现存在一些问题。现在请用你的脑洞再创造一个节点盒子,其数据为6。我们调用addhead方法,self.root指向了新的盒子,此时旧盒子被抛弃了。这显然不是我们想要的,我们想让节点们形成一条链,而不是顾此失彼。
我们稍微修改一下,使它们连成一条,而不是分开。
def addhead(self,data):
node=Node(data)
self.root.left=node
node.right=self.root
self.root=node
首先我们新建了一个节点,旧节点的left属性首先连接了新节点的位置,新节点的right属性又连接了旧节点的位置,Link对象的root属性不再连接旧节点而是重新连接了新节点的位置。这样,经过几步操作,我们得到了如图所示的结构。这种方法就是链表中的头插法。我们可以用这种方法插入无数个节点,直到耗尽计算机内存。
四、定位节点
假设我们已经用上面的方法插入了10000个节点,我们怎么把它们读出来呢?第一个数据,当然是Link.root.data,第二个数据,Link.root.right.data,第三个数据,Link.root.right.right.data,同理,第10000个数据,Link.root.right(省略9998个.right)......data就可以拿到第10000个数据了。这样的方法显然是有点麻烦的,为了不被老板开除,我们需要一个通用的方法。思路是这样的,我们定位一个节点,得到right属性,通过right定位下一个节点,以此循环直到right为None或者符合我们的要求。我们下面写一个定位节点位置的方法。这个方面我们后面会多次用到。
def find_node(self,n):
# 链表不为空时,开始后面的操作
if self.root:
# 定位第一个节点
temp=self.root
# 如果传入了-1,这里我们认为是查找最后一个节点
if n==-1:
while True:
# 如果right不为空,我们会一直把right赋值为temp,直到......
if temp.right:
temp=temp.right
# 直到right已经为空了,说明我们到达了链表的最后一个节点
# 这时候我们可以把temp返回给调用者了,调用者会对这个节点做一些事情
# 具体拿来做什么事情,不是这个方法所关心的
else:
return temp
else:
# 如果调用者没有传入-1,我们认为调用者希望找到第n个节点
i=0
# i在下面会累加,直到找到第n个节点
while i<n:
if temp.right:
temp=temp.right
i+=1
# 如果在找到n节点之前right就已经空了,说明链表没有调用者期望的这么长
# 此时,我们返回一个None告诉调用者查找失败
else:
return None
# 如果顺利定位了第n节点,我们会返回这个节点
return temp
# 查找之初如果链表是空的,也就无从查找,我们直接返回失败标志即可
else:
return None
我们成功定位了第n个节点!现在你能想通尾插法和中间插法的逻辑吗?
五、尾插法
def addend(self,data):
# 新建一个节点
node=Node(data)
# 如果Link不为空,进行下面的操作
if self.root:
# 找到最后一个节点
temp=self.find_node(-1)
# 如果查找成功时...
if temp:
# 原来的最后节点的right连接新节点
temp.right=node
# 新节点的left连接原来的最后节点
node.left=temp
# 如果查找失败,也就无从下手,直接返回失败标志
else:
return None
# 如果链表为空,直接让Link连接新节点就行了
else:
self.root=node
-------------------------------------------------------------------------------------------
今天先写到这里,不知道这样写能不能让人理解清楚。如果可以帮到你,请告诉我,我会继续更新其他的方法。