数据结构(Python实现)之链表

理解数据结构最好的方式就是用脑洞把它想象出来。

一、节点

class Node():
    def __init__(self,data=None):
        self.data=data
        self.left=None
        self.right=None

node = 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

-------------------------------------------------------------------------------------------

今天先写到这里,不知道这样写能不能让人理解清楚。如果可以帮到你,请告诉我,我会继续更新其他的方法。

猜你喜欢

转载自blog.csdn.net/qq_40878431/article/details/82991854