链表的增删改查(python实现)

链表的增删改查

使用python实现链表的增删改查

    • add(val):在头结点处增加,左插入
    • append(val):在尾结点处增加,右插入
    • remove_single(target):删除值为target的第一个节点
    • remove_all(target):删除值为target的所有节点
    • search(target):返回值为target的第一个节点索引
    • get(index):返回索引位置为index的第一个节点的值
# -*-coding:utf-8-*-
# 链表的增删改查
class Node:
    def __init__(self, val: int) -> None:
        self.val = val
        self.next: Node | None = None


class LinkedList:
    def __init__(self) -> None:
        self._head = None

    def is_empty(self):
        """判断是否为空"""
        return self._head is None

    def get(self, index: int) -> int | None:
        """获取index位置的值"""
        if self.is_empty():
            print('linked list is empty')
            return

        if index < 0 or index > self.size - 1:
            raise IndexError('index out of range!')

        cur = self._head

        for _ in range(index):
            cur = cur.next

        return cur.val

    def add(self, val: int):
        """在头部插入节点"""
        node = Node(val)
        node.next = self._head
        self._head = node

    def append(self, val: int):
        """在末尾插入节点"""
        node = Node(val)

        # 头结点特殊处理
        if self._head is None:
            self._head = node
            return

        cur = self._head

        # notes: 使用cur.next可以保证节点在末尾位置的前一个停下来
        while cur.next:
            cur = cur.next
        cur.next = node

    @property
    def size(self) -> int:
        _size = 0
        cur = self._head
        while cur:
            _size += 1
            cur = cur.next
        return _size

    def insert(self, pos, val) -> bool:
        if pos < 0 or pos > self.size:
            raise IndexError('pos out of range, please it check out!')
            # return False

        # 插入头结点
        if pos == 0:
            self.add(val)
            return True

        # 插入头结点方法2
        # 第二种方法会出错,原因在于这里的cur是一个副本,
        # 而并不是对self._head的引用,两者指向同一个数字而已
        # if pos == 0:
        #     node = Node(val)

        #     cur = self._head
        #     node.next = cur
        #     # 不要以为这里的cur和self._head是一个东西
        #     # 因为这个不是列表类似的容器,所以会产生复制
        #     cur = node
        #     print(self._head is cur) # False
        #     return

        node = Node(val)
        cur = self._head
        # 在指定位置的前一个停下来,方便进行赋值
        for _ in range(pos - 1):
            cur = cur.next
        node.next = cur.next
        cur.next = node
        return True

    def remove_single(self, target: int):
        """删除第一个值为target的节点"""
        if self._head == target:
            return self._head.next

        cur = self._head
        while cur.next and cur.next.val != target:
            cur = cur.next
        if cur.next is None:
            print('target not found in the linked list!')
            return self._head

        # 找到目标节点
        cur.next = cur.next.next
        return self._head

    def remove_single2(self, target: int):
        dummy_head = Node(-1)
        dummy_head.next = self._head
        cur = dummy_head

        while cur.next and cur.next.val != target:
            cur = cur.next
        if cur.next is None:
            print('target not found in the linked list!')

        # 找到匹配节点
        cur.next = cur.next.next

        return dummy_head.next

    def remove_single3(self, target: int):
        """删除第一个值为target的节点"""
        dummy_head = Node(-1)
        dummy_head.next = self._head
        cur = dummy_head

        while cur.next:
            if cur.next.val == target:
                cur.next = cur.next.next
                return dummy_head.next
            else:
                cur = cur.next

        print('target not found!')

        return dummy_head.next

    def remove_single4(self, target: int):
        """删除第一个值为target的节点"""
        dummy_head = Node(0)
        dummy_head.next = self._head
        cur = dummy_head

        while cur.next:
            if cur.next.val == target:
                cur.next = cur.next.next
                break
            else:
                cur = cur.next

        if cur.next is None:
            print('target not found!')

        return dummy_head.next

    def remove_single5(self, target: int):
        """删除第一个值为target的节点"""
        if self._head.val == target:
            return self._head.next

        pre, cur = self._head, self._head.next
        while cur and cur.next != target:
            pre = cur
            cur = cur.next

        if cur:
            pre.next = cur.next
        else:
            print('target not found!')
        return self._head

    def remove_all1(self, target: int):
        dummy_head = Node(0)
        dummy_head.next = self._head
        cur = dummy_head

        while cur.next:
            if cur.next.val == target:
                cur.next = cur.next.next
            else:
                cur = cur.next
        # the most important step
        self._head = dummy_head.next
        return dummy_head.next

    def remove_all2(self, target: int):
        while self._head and self._head.val == target:
            self._head = self._head.next

        if self._head is None:
            return None

        cur = self._head

        while cur.next:
            if cur.next.val == target:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return self._head

    def remove_all3(self, target: int):
        while self._head and self._head.val == target:
            self._head = self._head.next

        if self._head is None:
            return None

        pre, cur = self._head, self._head.next
        while cur:
            if cur.val == target:
                pre.next = cur.next
                cur = cur.next
            else:
                pre = cur
                cur = cur.next
        return self._head

    def pop(self, index: int) -> int:
        pass

    def search(self, target: int) -> int | None:
        if self.is_empty():
            print('linked list is empty!')
            return None

        cur = self._head
        index = 0
        while cur:
            if cur.val == target:
                return index
            cur = cur.next
            index += 1
        print('not found!')
        return None

    def __str__(self) -> str:
        """重写str方法,方便打印输出"""
        if self.is_empty():
            return 'linked list is empty!'

        arr = []
        cur = self._head
        while cur:
            arr.append(str(cur.val))
            cur = cur.next
        return '-->'.join(arr)


if __name__ == "__main__":
    linked_list = LinkedList()
    for val in range(10):
        linked_list.append(val)
    print(linked_list)

    linked_list.insert(0, 3)
    print(linked_list)

    linked_list.add(3)
    print(linked_list)

    linked_list.remove_all1(3)
    print(linked_list)

    for i in range(5):
        linked_list.append(i)
    print(linked_list)

    index = linked_list.search(3)
    print(index)

总结与思考:

  1. 对于链表的插入,一般使用cur.next进行循环,在目标值位置的前一个位置停下来
  2. 删除分为2大类,删除单个节点和所有节点,后者需要考虑的方面更多,尤其是出现所有值相等的链表。
    1. 链表删除方法一:头插法(dummy_head),后续步骤仅需考虑cur.next
    2. 链表删除方法二:特殊处理头结点,考虑完头结点后步骤与头插法一致

遇到的问题以及警告:

  1. 使用cur = self._head是实现的是对self._head的拷贝,使用cur可以调整后续指针的指向,但是涉及到头结点处理的时候,必须使用self._head进行处理,否则cur处理不会生效。

例如在指定位置插入节点时有如下代码:

if pos == 0:
    node = Node(val)
    cur = self._head
    node.next = cur
    cur = node 		# 1
    return
if pos == 0:
    node = Node(val)
    cur = self._head
    node.next = cur
    self._head = node 	# 2
    return

上述代码的作用是实现在头结点处插入节点,但是只有2能成功,1不能。
在这里插入图片描述
从上面的图像中可以看到,self._headcur都是指向头结点的,所以当使用cur进行遍历以及后续节点的修改的时候,两者没有区别,不直接使用self._head是为了防止修改之后链表找不到头结点,于是往往使用一个副本cur进行替代。

但是当涉及到头结点的处理的时候,需要特别注意,否则错误导致修改无法传递到真正的头结点,上面代码1和代码2的图像如下:
在这里插入图片描述
在这里插入图片描述
从图像中可以看到,代码2完成了对self._head的修改,所以成功,代码1只是对cur进行了修改,此时作用没有传递到self._head导致出错。

  1. 在删除所有节点的时候,
    def remove_all1(self, target: int):
        dummy_head = Node(0)
        dummy_head.next = self._head
        cur = dummy_head

        while cur.next:
            if cur.next.val == target:
                cur.next = cur.next.next
            else:
                cur = cur.next
        # the most important step
        self._head = dummy_head.next
        return dummy_head.next

如果需要将cur的修改传递到self._head,需要执行如下语句:

self._head = dummy_head.next

否则无法删除头结点处值等于target的节点,原因与上面一致,对头结点操作一定要传递!否则修改仅仅在临时分支上。

在这里插入图片描述
如上图所示,如果使用dummy_head删除了前两个节点,不执行self._head = dummy_head.next进行修改传递的话,那么self._head只能获取到非头结点的修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/166013.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux僵尸进程

Linux僵尸进程 一、僵尸进程简介二、僵尸进程的危害三、避免僵尸进程的方法 一、僵尸进程简介 如果父进程比子进程先退出&#xff0c;子进程将被1号进程托管&#xff08;这也是一种让程序在后台运行的方法&#xff09;。如果子进程比父进程先退出&#xff0c;而父进程没有处理…

基于SSM的“鲜花”电子商务平台设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

通过汇编理解cortex-m3:第0章

第0章&#xff1a;准备工作 基本想法&#xff1a;利用汇编和gdb调试&#xff0c;来学习cortex-m3汇编指令&#xff0c;以及一些寄存器的功能。 软件和硬件&#xff1a; 硬件&#xff1a;韦东山瑞士军刀中的最小核心板&#xff08;STM32F103C8T6&#xff09; STLINK-V2&#…

基于java web个人财务管理系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

PyCharm:PyCharm新建.py文件时自动带出指定内容

在pycharm中加上指定内容&#xff0c;每次新建.py文件都会自动带出指定内容 操作&#xff1a; File—Setting—Editor----File and Code Templates--Python Script 在右侧窗口中加上如下信息 # encoding: utf-8 # author: Jeffrey # file: ${NAME}.py # time: ${DATE} ${TI…

【Java SE】循环一些基本练习

判定一个数字是否是素数 public class Test {public static int is_sushu(int n) {if(n 1) {return 0;}int i ;for (i 2; i < Math.sqrt(n); i) {if(n % i 0 ) {break;}}if (i > n) {return 1;}return 0;}public static void main(String[] args) {Scanner scanner …

kafka 磁盘扩容与数据均衡实在操作讲解

文章目录 一、概述1&#xff09;Kafka 磁盘扩容概述2&#xff09;Kafka 数据均衡概述 二、K8s 集群部署三、kafka on k8s 环境部署1&#xff09;安装 helm2&#xff09;安装 zookeeper1、添加源并下载部署包2、修改配置3、开始安装 zookeeper4、测试验证5、卸载 3&#xff09;安…

uview-plus中二级菜单左右联动更改为uni-app+vue3+vite写法

uview-plus3.0重磅发布&#xff0c;全面的Vue3移动组件库 该插件使用的vue2写法&#xff0c;但支持vue3引用&#xff0c;在此基础上修改为uni-appvue3vite; <template><view class"u-wrap mainClass"><!-- <back-header :title"pageTitle&quo…

【Linux】-进程间通信-命名管道文件(没有关系的进程间进行通信),以及写一个日志模板

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

mysql数据库【进阶篇】

1.存储引擎 1.1 mysql的体系结构 连接层&#xff1a;最上层是一些客户端和链接服务&#xff0c;主要完成- -些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。服务层&#xff1a;第二层架构主要完成大多数的核心服务功…

【教学类-06-06】20231118 (55格版)X以内加法、减法、加减混合题

背景需求 1、长期做手工制作&#xff0c;常规管理难以控制 优势&#xff1a; 1、幼儿创作热情高涨&#xff0c;发明的新玩具多 2、互助观摩&#xff0c;进一步模仿、创作作品 3、互动游戏兴趣浓厚&#xff0c;语言交流踊跃&#xff0c; 劣势&#xff1a; 1、纸条碎片多&…

深入流行推荐引擎3:Spotify音乐推荐系统

深入流行推荐引擎3&#xff1a;Spotify音乐推荐系统 Spotify音乐推荐系统通过矩阵分解发现每周&#xff08;Discover Weekly via Matrix Factorization&#xff09;Discover Weekly 如何运作&#xff1f;&#xff08;How Discover Weekly Works?&#xff09;矩阵分解&#xff…

【IPC】 共享内存

1、概述 共享内存允许两个或者多个进程共享给定的存储区域。 共享内存的特点 1、 共享内存是进程间共享数据的一种最快的方法。 一个进程向共享的内存区域写入了数据&#xff0c;共享这个内存区域的所有进程就可以立刻看到 其中的内容。 2、使用共享内存要注意的是多个进程…

使用持久卷部署 WordPress 和 MySQL

&#x1f5d3;️实验环境 OS名称Microsoft Windows 11 家庭中文版系统类型x64-based PCDocker版本Docker version 24.0.6, build ed223bcminikube版本v1.32.0 &#x1f587;️创建 kustomization.yaml 你可以通过 kustomization.yaml 中的生成器创建一个 Secret存储密码或密…

Android问题笔记四十六:解决open failed: EACCES (Permission denied) 问题

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列点击跳转>ChatGPT和AIGC &#x1f449;关于作者 专…

Jmeter性能实战之分布式压测

JMeter分布式执行原理 1、JMeter分布式测试时&#xff0c;选择其中一台作为调度机(master)&#xff0c;其它机器作为执行机(slave)。 2、执行时&#xff0c;master会把脚本发送到每台slave上&#xff0c;slave 拿到脚本后就开始执行&#xff0c;slave执行时不需要启动GUI&…

深度学习YOLO抽烟行为检测 - python opencv 计算机竞赛

文章目录 1 前言1 课题背景2 实现效果3 Yolov5算法3.1 简介3.2 相关技术 4 数据集处理及实验5 部分核心代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习YOLO抽烟行为检测 该项目较为新颖&#xff0c;适合作为竞赛课…

Parity Game——种类并查集、权值并查集、离散化

题目描述 思路 怎么得到这个序列中每一段的关系&#xff1f; 我们可以把这个只包含0和1的序列看作一个数组&#xff0c;0表示当前位置为0&#xff0c;1表示当前位置为1&#xff0c;利用前缀和的性质可以知道某一段中所包含的1的数量sum1 a[r] - a[l-1] 如果sum1为偶数&…

Rust开发——切片(slice)类型

1、什么是切片 在 Rust 中&#xff0c;切片&#xff08;slice&#xff09;是一种基本类型和序列类型。在 Rust 官方文档中&#xff0c;切片被定义为“对连续序列的动态大小视图”。 但在rust的Github 源码中切片被定义如下&#xff1a; 切片是对一块内存的视图&#xff0c;表…

如何隐藏Selenium特征实现自动化网页采集

Selenium是一个流行的自动化网页测试工具&#xff0c;可以通过模拟用户在Chrome浏览器中的操作来完成网站的测试。然而&#xff0c;有些网站会检测浏览器是否由Selenium驱动&#xff0c;如果是&#xff0c;就会返回错误的结果或拒绝访问。为了避免这种情况&#xff0c;我们需要…