Python实现单向链表(上):小白写法的15种增删改查操作

其他链表相关文章:Python链表中环的问题:三种方法判断是否有环,找到入环节点,…

下一篇:Python实现单向链表(下):整合式实现增删改查操作

链表简介

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表和数组区别:链表不需要一系列连续的内存

链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:

  1. 一个是存储数据元素的数据域
    (在下面代码中用data存储)
  2. 另一个是存储下一个结点地址的指针域
    (在下面代码中用next存储)

链表优点

  1. 链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。

  2. 链表克服了数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。

链表缺点

  1. 失去了数组随机读取的优点
  2. 链表由于增加了结点的指针域,空间开销比较大。

单向链表

结点的指向是单向的,从前一个结点指向后一个结点

碎碎念:是节点还是结点?

转自:《【数据结构】节点和结点,到底怎么区分?》
一篇文章彻底把区别说清楚了,感激涕零

节点呢,被认为是一个实体,有处理能力,比如,网络上的一台计算机;而结点则只是一个交叉点,像“结绳记事”,打个结,做个标记,仅此而已,还有就是,要记住:一般算法中点的都是结点。

大体思路

需要写两个类:

  1. Node类:用于创建结点,并将结点以(人类)能看懂的字符串形式输出,而不是显示内存地址
  2. LinkedList类:用于将各结点连成链表,并实现对链表进行操作的一些方法

代码

创建Node类:
class Node:
    def __init__(self, data, next=None):
        self.data = data # 数据,当前结点的元素
        self.next = None # 指针,指向下一个结点

    def __repr__(self): # 把结点表示为一个字符串
        return "NodeValue({})".format(self.data)

调用测试:

n = Node(1)
print(n) # 因为写了__repr__方法,所以输出的是Node(1),而不是内存地址
创建LinkedList类:

准备工作:初始化方法 & 打印字符串方法
想要达到的输出效果类似于:
NodeValue(1) --> NodeValue(3) --> NodeValue(4) --> END

class LinkedList:
    def __init__(self):
        self.head = None # 初始化头结点

    ## 打印链表
    def __repr__(self):
        cursor = self.head
        string_repr = ""
        while cursor:
            string_repr += f"{cursor} --> "
            cursor = cursor.next
        return string_repr + "END"
「查」:方法

1. is_empty() 判断链表是否为空

    def is_empty(self):
        return self.head == None

2. length() 返回链表长度

    def length(self):
        if self.head is None:
            return 0
        cursor = self.head
        count = 0
        while cursor:
            count += 1
            cursor = cursor.next
        return count

3. exist(data) 判断结点是否存在

    def exist(self, data):
        """
        :return: True or False
        :rtype: Boolean
        """
        cursor = self.head
        while cursor:
            if cursor.data == data:
                return True
            else:
                cursor = cursor.next
        return False

4. travel() 遍历整个链表

    def travel(self):
        if self.head is None:
            return None
        cursor = self.head
        while cursor:
            print(cursor)
            cursor = cursor.next

5. getitem(index) 根据索引,得到链表中该位置的值

    def getitem(self, index):
        current = self.head
        if current is None:
            raise IndexError("This is an empty linked list.")
        for i in range(1, index):
            # 目标超过链表范围
            if current.next is None:
                raise  IndexError("Index out of range")
            current = current.next
        return current

6. search(data) 返回该结点出现的左数第一个的位置,不存在返回-1

    def search(self, data):
        if self.exist(data) == False:
            return -1
        else:
            cursor = self.head
            count = 0
            while cursor:
                count += 1
                if cursor.data == data:
                    return count
                else:
                    cursor = cursor.next
「增」:方法

7. insert_head(data) 在链表头部添加元素

    def insert_head(self, data):
        new_node = Node(data)  # 创建一个新结点
        if self.head: # 如果已存在头部结点
            # 让新创建的结点的next指向原本的头结点
            # 也就是说,新结点成为了原本的头结点的上一个结点
            new_node.next = self.head
        self.head = new_node # 重置头结点

8. append(data) 在链表尾部添加元素

    def append(self, data):
        if self.head is None:
            # 如果链表目前为空,则在头结点添加该元素
            self.insert_head(data)
        else:
            cursor = self.head
            while cursor.next: # 遍历链表
                cursor = cursor.next
                # 创建一个新结点添加到链表尾部
            cursor.next = Node(data)

9. insert(pos, data) 在链表中的指定位置添加元素

    def insert(self, pos, data):
        """
        :param pos: 要插入结点的位置,从0开始。
                    pos > 0,代表是从左开始数的位置
                    pos = 0,代表在头部添加
                    pos < 0,代表是从右开始数的位置,倒数从-1开始
                    也就是说 链表长度 + 倒数下标 = 正数下标
        :type pos: int
        :param data: 要插入的结点值
        :type data: int
        """
        if pos > 0:
            if self.head is None:
                # 如果链表目前为空,则在头结点添加该元素
                self.insert_head(data)
            else:
                new_node = Node(data)  # 创建一个新结点

                # 通过循环,得到新结点的上一个结点
                pre = self.head
                count = 0
                while count < (pos-1):
                    count += 1
                    pre = pre.next
                # 让新结点指向原本该位置的结点
                new_node.next = pre.next
                # 让上一个结点指向新结点
                pre.next = new_node
        elif pos == 0:
            self.insert_head(data)
        elif pos < 0:
            if self.head is None:
                # 如果链表目前为空,则在头结点添加该元素
                self.insert_head(data)
            else:
                new_node = Node(data)
                # 通过循环,得到新结点正数顺序的上一个结点
                leftpos = self.length() + pos
                pre = self.head
                count = 0
                while count < (leftpos - 1):
                    count += 1
                    pre = pre.next
                # 让新结点指向原本该位置的结点
                new_node.next = pre.next
                # 让上一个结点指向新结点
                pre.next = new_node
「删」:方法

10. delete_head() 删除头结点

    def delete_head(self):
        temp = self.head
        if self.head:
            self.head = temp.next
            temp.next = None

11. delete_tail() 删除尾结点

    cursor = self.head
        if self.head:
            if self.head.next is None:
                self.head = None
            else:
                while cursor.next.next:
                    cursor = cursor.next
                cursor.next, cursor = None, cursor.next
        elif self.head == None:
            return "This is an empty linked list."

12. remove(data) 删除结点,若存在值相等的结点,仅删除左起第一个

    def remove(self, data):
        """
        :return: 返回删除结点后的链表;若传入的数据在链表中不存在,则返回原链表
        """
        if self.exist(data) == False:
            return self
        else:
            pre = self.head
            cursor = pre.next
            count = 0
            while cursor:
                if cursor.data == data:
                    # 删除,也就是让该结点的上一结点直接连到该结点的下一结点
                    pre.next = cursor.next
                    cursor.next = None
                    return self
                else:
                    pre = pre.next
                    cursor = cursor.next

13. removeall(data) 删除结点,若存在值相等的结点,删除所有

    def removeall(self, data):
        """
        :return: 返回删除结点后的链表;若传入的数据在链表中不存在,则返回原链表
        """
        while self.exist(data):
            self.remove(data)
        return self
「改」:方法

14. setitem(index, data) 根据索引,为链表中该位置的值重新赋值

    def setitem(self, index, data):
        current = self.head
        if current is None:
            raise IndexError("This is an empty linked list.")
        for i in range(1, index):
            # 目标超过链表范围
            if current.next is None:
                raise  IndexError("Index out of range")
            current = current.next
        current.data = data
其他方法

15. linklist(object) 传入对象,一次性将序列中的所有元素构建成一个链表

    def linklist(self, object):
        self.head = Node(object[0])
        temp = self.head
        for i in object[1:]:
            node = Node(i)
            # 将上一个结点指向下一个结点
            temp.next = node
            temp = temp.next

下一篇:Python实现单向链表(下):整合式实现增删改查操作


Info
Author: Shanshan Yan
Wechat: shanshan700224
Copyright: All rights reserved
  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值