踏踏实实地打好数据结构—链表

243 阅读4分钟

学习的一点技巧,通常我们在学习一个新的东西,开始的时候动力十足,雄心勃勃。因为每一个新的领域在入门时候都相对简单,所以在开始学习某一个新的东西往往很舒服,一路高歌猛进,势如破竹。但是接下来随着深入,就进入了瓶颈期,难点一个一个浮出水面,我们就开始怀疑花这么多时间是否值当。

链表的概述

为什么引入链表

  • 对于顺序表结构在插入或者删除结点时,往往需要移动大量的数据

链表的定义

  • 序列形的数据结构
  • 结点通常包含两个部分,包含数据和指向下一个结点的指针
  • 链表结构就是由许多这种结点构成的。首先需要定义一个”头引用“变量,该引用变量指向链表结构的第一个结点。

链表和数组对比

  • 链表存储更加灵活,无需像数组那样申请连续的存储空间。

链表的类型

  • 单链表: 同上面的链式结构一样,每个结点中只包含一个引用
  • 双向链表: 若每个结点包含两个引用,一个指向下一个结点,另一个指向上一个结点
  • 循环链表:

链表支持基本操作

  • 插入: 将一个新的元素插入链表的任意位置
  • 删除: 将一个元素从链表中删除
  • 查找: 查找一个特定的元素
  • 更新: 更新一个特定结点上的元素
  • 添加: 添加结点

代码实现

实现 Node 类

首先我们创建一个 Node 类,其中定义 data 属性用于保持数据,另一个 next 属性指向下一个结点。

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

实现 LinkedList 类

LinkedList 是用于将一个结点链接起来形成一个数据结构。在初始化方法__init__中初始化 head 结点,创建一个没有数据的结点作为 head 结点。这个结点通常是用户访问不到的结点,head 结点类似一个占位符用于接受结点,所以 head 结点在 LinkedList 初始化时就自动创建而非用户添加的结点。

class LinkedList:
    def __init__(self):
        self.head = Node()

append 方法实现

添加一个结点到列表中,首先我们将当前指针指向 head 结点,然后通过判断当前结点的 next 属性是否为空来遍历到链表尾部,当 cur 结点的 next 属性为空则说明 cur 指针移动到链表尾部。

    def append(self,data):
        new_node = Node(data)
        cur = self.head
        while cur.next != None:
            cur = cur.next
        cur.next = new_node

length 方法实现

如果理解了 append 实现中遍历链表的方式,这里 length 方法实现也就不难理解。

    def length(self):
        cur = self.head
        total = 0
        while cur.next!=None:
            total += 1
            cur = cur.next
        return total

display 方法实现

这是一个链表可视化的方法,代码中没有什么特别的。

    def display(self):
        elems = []
        cur_node = self.head
        while cur_node.next != None:
            cur_node=cur_node.next
            elems.append(cur_node.data)
        print(elems)

get 方法的实现

get 方法接收一个索引,也就是根据索引返回一个对应结点,我们 head 结点是没有索引的,首先我们先判断一下 index 是否超出了链表长度,如果超出链表的长度则抛出一个异常警告ERROR:'GET' Index out of range!并返回一 None。首先 cur_node 当前结点指向 head 结点,然后在 while 循环中,首先将当前结点指向当前结点下一个结点,然后在去判断 cur_idx 是否为 index 所以当 index 为 0 时候当前结点已经第一个添加结点而非 head 结点了,这里大家可以自己琢磨一下

    def get(self,index):
        if index>=self.length():
            print("ERROR:'GET' Index out of range!")
            return None
        cur_idx=0
        cur_node = self.head
        while True:
            cur_node=cur_node.next
            if cur_idx==index:return cur_node.data
            cur_idx+=1

delete 方法的实现

    def delete(self,index):
        if index>=self.length():
            print("ERROR:'Delete' Index out of range!")
            return
        cur_idx=0
        cur_node = self.head
        while True:
            last_node=cur_node
            cur_node = cur_node.next
            if cur_idx==index:
                last_node.next = cur_node.next
                return
            cur_idx+=1
            

删除我们需要将上一个结点 next 指向当前结点的下一个结点从实现了删除当前 index 所对应的结点的效果。

if __name__ == "__main__":
    my_list = LinkedList()
    my_list.append(1)
    my_list.append(2)
    my_list.append(3)
    my_list.append(4)
    my_list.append(5)
    print(my_list.get(0))
    my_list.delete(3)
    my_list.display() #[1, 2, 3, 5]

update 方法

这个方法和 get 方法基本没有什么差别就在当找到对应索引的结点时,对结点进行更新。

    def update(self,index,data):
        if index>=self.length():
            print("ERROR:'Update' Index out of range!")
            return
        cur_idx = 0
        cur_node = self.head
        while True:
            cur_node=cur_node.next
            if cur_idx==index:
                cur_node.data = data
                return
            cur_idx+=1