【chap4-链表】用Python3刷《代码随想录》

news2024/11/19 14:29:33

通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域data,另一个是指针域next(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针)

链接的入口点称为链表的头节点head

单链表

1、链表的类型

三种类型:单链表、双链表、循环链表 

双链表

循环链表

2、链表的存储方式

数组在内存中是连续分布的,但链表在内存中不是连续分布的,链表是通过指针域的指针来链接内存中各个节点的。即:链表的节点在内存中是分散存储的,通过指针连在一起

3、链表的定义

# Python定义链表的节点
class ListNode:
    def __init__(self, val, next=None):
        self.val = val
        self.next = next

4、链表的操作

(1)删除节点:

此时节点D依然存留在内存中,只不过从链表中被移除了而已。Python有自己的内存回收机制,不用手动释放

(2 )添加节点:

链表的添加和删除都是时间复杂度为 O(1) 的操作,不会影响其他节点。但是,删除或者添加某个节点的前提是找到操作节点的前一个节点,而查找前一个节点的时间复杂度是 O(n)

5、性能分析

在定义数组的时候,长度是固定的,如果想改动数组的长度,则需要重新定义一个新的数组; 

链表的长度可以是不固定的,并且可以动态增删,适合数据量不固定、频繁增删、较少查询的场景


203. 移除链表元素 

203. 移除链表元素

删除元素就是让节点的 next 指针直接指向下一个节点的下一个节点,因为单链表的特殊性,所以需要找到操作节点的前一个节点

两种链表操作方式:

  • 直接使用原来的链表执行删除操作:删除头节点和删除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来删除当前节点的,而头节点没有前一个节点,只要将头节点向后移动一位即可
  • 设置一个虚拟头节点再执行删除操作:设置一个虚拟头节点dummy_head,这样原链表的所有节点都可以按照统一的方式删除了。新的头节点是dummy_head.next
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        # 设置一个虚拟头节点
        dummy_head = ListNode(next=head)
        # 设置指针位置
        cur = dummy_head
        while cur.next != None:
            if cur.next.val == val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return dummy_head.next

707. 设计链表

707. 设计链表

这里以单链表为例。注意,在定义链表之前需要先定义链表节点的结构体

(1)获取下标为 index 的节点的值:注意 cur 初始化为 dummy_head.next,也就是真正的head

(2)头部插入节点:注意操作顺序,十分重要!!!还要记得 size 要 +1

(3)尾部插入节点:记得 size 要 +1

(4)第n个节点前插入节点:操作第n个点,第n个点一定是cur_next,这样才能用 cur 去控制第n个点是增加还是删除。注意操作顺序,十分重要!!!以及记得 size 要 +1

(5)删除下标为 index 的节点:删除下标为 index 的节点(cur.next),必须要知道它的前一个节点(cur),让它的前一个节点指向该节点的后一个节点,才能删除该节点。以及记得 size 要 -1

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next


class MyLinkedList:

    def __init__(self):
        self.dummy_head = ListNode(0)
        self.size = 0


    def get(self, index: int) -> int:
        if index<0 or index>self.size-1:
            return -1
        cur = self.dummy_head.next
        while index:
            cur = cur.next
            index-=1
        return cur.val


    def addAtHead(self, val: int) -> None:
        new_node = ListNode(val)
        new_node.next = self.dummy_head.next
        self.dummy_head.next = new_node
        self.size+=1


    def addAtTail(self, val: int) -> None:
        new_node = ListNode(val)
        cur = self.dummy_head
        while cur.next != None:
            cur = cur.next
        cur.next = new_node
        self.size+=1


    def addAtIndex(self, index: int, val: int) -> None:
        if index>self.size:
            return
        else:
            new_node = ListNode(val)
            cur = self.dummy_head
            while index:
                cur = cur.next
                index-=1
            new_node.next = cur.next
            cur.next = new_node
            self.size+=1


    def deleteAtIndex(self, index: int) -> None:
        if index<0 or index>self.size-1:
            return
        cur = self.dummy_head
        while index:
            cur = cur.next
            index-=1
        cur.next = cur.next.next
        self.size-=1


# Your MyLinkedList object will be instantiated and called as such:
# obj = MyLinkedList()
# param_1 = obj.get(index)
# obj.addAtHead(val)
# obj.addAtTail(val)
# obj.addAtIndex(index,val)
# obj.deleteAtIndex(index)

206. 反转链表

206. 反转链表

法1:双指针法

  • cur 先指向 head,这是第一个指针
  • 第二个指针,就是反转之后,cur指向它的后面了,定义为 pre(其实就是 cur 的前一位),初始化为空指针
  • 遍历链表,直到 cur 指向空指针结束
  • 遍历时,需要第三个指针 tmp,在没反转之前,保存 cur 的下一个指针,再反转
  • 最后新链表的头节点就是 pre
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur = head
        pre = None 
        while cur != None:
            tmp = cur.next
            cur.next = pre  # 改变方向
            # 指针往后遍历
            pre = cur
            cur = tmp
        return pre

法2:递归法

同样是当 cur 为空的时候循环结束,不断将 cur 指向 pre

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:

        def revserse(cur, pre):
            if cur == None:
                return pre
            tmp = cur.next
            cur.next = pre
            return revserse(tmp,cur)
        
        return revserse(head, None)

24. 两两交换链表中的节点

24. 两两交换链表中的节点

几个重点: 

(1)操作指针一定要指向要反转的两个节点的前一个结点,如要交换节点1和节点2,此时操作指针应指向dummy_head(节点1的前一个节点)

(2)遍历什么时候结束?    while cur.next != None and cur.next.next != None:

  • 链表节点个数为偶数时,cur.next = null
  • 链表节点个数为奇数时,cur.next.next = null
  • 链表节点个数为0,即空链表时,cur.next = null(此时cur指向虚拟头节点)

(3)交换相邻两个元素时,要注意操作的先后顺序

  • 初始时,cur指向虚拟头节点

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy_head = ListNode(next=head)   # 创建虚拟头结点
        cur = dummy_head   # 当前操作指针指向虚拟头结点

        while cur.next != None and cur.next.next != None:
            tmp = cur.next   # 保存节点1
            tmp1 = cur.next.next.next   # 保存节点3
            cur.next = cur.next.next   # 虚拟头结点指向节点2
            cur.next.next = tmp   # 节点2指向节点1
            tmp.next = tmp1   # 节点1指向节点3
            cur = cur.next.next   # 将当前操作指针往后移动2位

        return dummy_head.next   # 返回交换后链表的头节点

19. 删除链表的倒数第N个结点 

19. 删除链表的倒数第 N 个结点

要删某个节点,操作指针要指向该节点的前一个节点

如何找到倒数第N个节点?

  • 虚拟头节点:好处是不需要对操作节点是不是头节点进行特殊的判断,而可以采用统一的方式进行删除   dummy_head = ListNode(next=head)
  • 定义快慢指针,让快指针先移动N步,然后快慢指针再同时移动,直到快指针指向了空节点,此时慢指针就指向了要删的倒数第N个节点
  • 但问题是,要删倒数第N个节点,操作指针要指向该节点的前一个节点,即慢指针应指向倒数第N个节点的前一个节点
  • 故最终:定义快慢指针,让快指针先移动N+1步,然后快慢指针再同时移动,直到快指针指向了空节点,此时慢指针就指向了要删的倒数第N个节点的前一个

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        # 定义虚拟头节点
        dummy_head = ListNode(next=head)
        # 定义快慢指针,初始值都为虚拟头节点
        fast = slow = dummy_head

        # 让快指针先走n+1步
        for i in range(n+1):
            fast = fast.next
        # 然后快慢指针再同时走,直到快指针为空
        while fast != None:
            fast = fast.next
            slow = slow.next
        # 此时慢指针指向倒数第n个节点的前一个
        slow.next = slow.next.next
        return dummy_head.next

160. 相交链表

160. 相交链表

注:交点指引用完全相同,即内存地址完全相同的交点 

若一个指针到达链表的结尾,就切换到另一个链表的头节点继续移动 

LeetCode刷题|python版本|160题|相交链表_哔哩哔哩_bilibili

分析:设第一个公共节点为node,链表A的节点数量为a,链表B的节点数量为b,两链表公共尾部的节点数量为c,则有 

  • 头节点 headA 到 node 前,共有 a - c 个节点
  • 头节点 headB 到 node 前,共有 b - c 个节点

考虑构建两个节点指针 curA、curB,分别指向两链表头节点 headA、headB,做如下操作:

  • 指针 curA 先遍历完链表A,再开始遍历链表B,当走到 node 时,共走步数为:a+(b-c)
  • 指针 curB 先遍历完链表B,再开始遍历链表A,当走到 node 时,共走步数为:b+(a-c)

可以发现它们走的步数相同,所以此时指针 curA 和 curB 重合,且有两种情况:

  • 若两链表有公共尾部(即c>0):指针 curA 和 curB 同时指向第一个公共节点node
  • 若两链表无公共尾部(即c=0):指针 curA 和 curB 同时指向null

故返回 curA 和 curB 任一指针即可

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        # 定义两指针,分别指向两链表的头节点
        curA = headA
        curB = headB

        # 两指针不相等时就同时向后移动
        while curA != curB:
            curA = curA.next if curA else headB   # 若curA走到空,跳到B
            curB = curB.next if curB else headA
        # 当两指针相等时返回任意一个指针即可
        return curA

算法复杂度:

  • 由于两指针最多遍历两个链表,故时间复杂度为O(m+n),其中m和n为两链表的长度
  • 由于不需要开辟额外空间,故空间复杂度为O(1)

142. 环形链表II 

142. 环形链表 II

这题有两小问:

  • 判断链表是否有环:快慢指针都从头节点出发,快指针每次走2步,慢指针每次走1步,若能相遇则有环
  • 寻找环的入口:在相遇节点处定义一个指针index1,在头节点处定一个指针index2,让index1和index2同时移动,每次移动一个节点,那么它们相遇的地方就是环的入口节点
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # 定义快慢指针
        fast, slow = head, head
        while fast != None and fast.next != None:    # 因为fast每次走两步
            fast = fast.next.next
            slow = slow.next
            if fast == slow:   # 若快慢指针相遇
                index1 = fast    # 相遇点
                index2 = head    # 头节点
                while index1 != index2:    # 两者相遇时终止
                    index1 = index1.next
                    index2 = index2.next
                return index1
        return None

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

耳夹式骨传导耳机有哪些比较好用?这三个款式不容错过!

骨传导耳机由于不入耳&#xff0c;不用担心耳道健康问题&#xff0c;越来越受到广大网友的喜欢&#xff0c;而传统的入耳式耳机&#xff0c;则因为长时间佩戴会耳朵痛&#xff0c;容易掉落等问题逐渐的被网友抛弃&#xff0c;那么在骨传导耳机市场种类这么多的情况下&#xff0…

Apache Kudu 在**医疗科技的生产实践

目录 说明 医疗场景下数据特点 KUDU 的介绍 kudu 架构 kudu 文件组织形式 kudu的生产实践 技术选型 整体的架构 项目遇到的问题 参考资料 说明 本文主要介绍APACHE KUDU 在**医疗科技数据实时分析场景下的实践&#xff0c;内容包括&#xff1a; 医疗场景下数据特点 …

mysql什么情况下行锁(表锁)(锁的概念)

1&#xff1a;数据表aa的设计结构 2&#xff1a; 使用navicat编写手动控制事务 3&#xff1a;先选择开启事务和执行更新操作&#xff0c;where b1&#xff08;表锁&#xff09;b不是索引&#xff0c;不提交事务&#xff0c;&#xff08;如果where b1&#xff0c;b是索引就行锁&…

本地Nginx部署React前端项目浅尝

目录 nginx [下载](http://nginx.org/en/download.html)nginx命令react打包文件放置nginx 配置 运行效果nginx踩坑根目录配置 nginx 下载 根据上面的版本找到适合自己的 nginx版本&#xff0c;我目前是环境是 windows&#xff0c;所以下载 稳定版本。 nginx命令 在下载的ngin…

数学建模-判断数据是否服从正态分布

大样本用qq图 >1000 皮尔逊相关系数需要正态性检验&#xff0c;利用上面三种方法其中一种 斯皮尔曼相关系数不用正态性检验

Claude2轻松解决代码Bug的实战方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

GPT-4最新细节曝光:从架构、基础设施、训练数据集、成本、视觉到MoE

OpenAI保持GPT-4架构封闭&#xff0c;不是因为对人类的某种存在风险&#xff0c;而是因为他们所构建的内容是可复制的。实际上&#xff0c;我们预计Google、Meta、Anthropic、Inflection、Character、Tencent、ByteDance、Baidu等公司在短期内将拥有与GPT-4一样甚至更强大的模型…

T100简易的查询作业功能开发

一、自定义作业维护 首先打开作业【azzi310】,进行作业的新增。 这是一个空白的作业界面,我们需要填写的地方也就那么几个。 查询单id:注册一个查询单 【四个字母三个数字】比如这里我们cxmq101.查询单名称:给这个查询起一个名字最大查询笔数:可以自己定义报表是否自己录…

数据库,数据仓库,数据湖

数据仓库四层分层 ODS——原始数据层&#xff1a;存放原始数据 ODS层即操作数据存储&#xff0c;是最接近数据源中数据的一层&#xff0c;数据源中的数据&#xff0c;经过抽取、洗净、传输&#xff0c;也就说传说中的ETL之后&#xff0c;装入本层&#xff1b;一般来说ODS层的数…

JavaScript初识

ECMAScript和JavaScript到底是什么关系&#xff1f; 简单来说&#xff0c;ECMAScript是JavaScript语言的国际标准&#xff0c;JavaScript是ECMAScript的实现。 一、第一个程序&#xff1a;hello word 二、JavaScript的几种常见写法&#xff1a; 1、将JavaScript写在标签上 2、…

应用上下文能否获取-spring13

我们能否通过web层通过spring容器去获得serive&#xff0c;然后serive内部Dao再去注入 这句话的意思是去加载xml配置文件&#xff0c;去加载spring容器&#xff0c;每次都要创建&#xff0c;太麻烦了&#xff0c;影响性能 最好的方法&#xff1a;应用上下文或者spring容器创建一…

DCL与延迟初始化(单例)

前言 在Java多线程程序中&#xff0c;有时候需要采用延迟初始化来降低初始化类和创建对象的开销。 第一种&#xff08;存在问题&#xff09; public class UnsafeLazyInitialization {private static Instance instance;public static Instance getInstance() {if (instance …

零基础如何自学成为网络安全工程师

前言 一份网络攻防渗透测试的学习路线&#xff0c;不藏私了&#xff01; &#x1f449; 【一帮助安全学习一】&#x1f448;这里自取256G网络安全自学资料 1、学习编程语言(phpmysqljshtml) 原因&#xff1a; phpmysql可以帮助你快速的理解B/S架构是怎样运行的&#xff0c…

【AI底层逻辑】——篇章5(上):机器学习算法之回归分类

目录 引入 一、何为机器学习 1、定规则和学规则 2、算法的定义 二、机器学习算法 1、常见学习方法 2、回归 3、分类 续下篇... 往期精彩&#xff1a; 引入 计算机发明初&#xff0c;专家通过将专业知识和经验梳理成规则输入计算机程序&#xff0c;但是这样跟不上知识…

IT技术培训班:搭乘学习快车的抉择

引言&#xff1a; 在IT技术学习的道路上&#xff0c;我们常常会被推荐各种五花八门的技术培训班。它们通过各种宣传手段向我们展示着美好的未来和无限的机会。然而&#xff0c;我们又应该如何看待这些培训班呢&#xff1f;在培训班里学技术真的有用吗&#xff1f;本文将从不同角…

【Java进阶之路】HashMap源码分析(JDK1.8)

概述 JDK 1.8 对 HashMap 进行了比较大的优化&#xff0c;底层实现由之前的 “数组链表” 改为 “数组链表红黑树”&#xff0c;本文就 HashMap 的几个常用的重要方法和 JDK 1.8 之前的死循环问题展开学习讨论。 JDK 1.8 的 HashMap 的数据结构如下图所示&#xff0c;当链表节…

Docker 替代方案:适用于 SaaS 应用程序的 10 种 Docker 替代方案

Docker技术已经在基础设施管理领域引起了革命性的变化&#xff0c;以至于Docker现在已经成为容器的代名词。重要的是要理解&#xff0c;所有的Docker都是容器&#xff0c;但并非所有的容器都是Docker。虽然Docker是最常用的容器技术&#xff0c;但还有其他几种替代Docker的选择…

积分兑换小程序项目总结

1. 项目概述 背景&#xff1a;中标项目&#xff0c;第三方公司做会员福利&#xff0c;以积分的形式发放。目标&#xff1a;给固定的钱&#xff0c;积分兑完&#xff0c;周期两个月。需求&#xff1a;固定会员能及时线上兑换积分。解决方案&#xff1a;开发微信小程序在线兑换。…

Nexpose v6.6.203 for Linux Windows - 漏洞扫描

Nexpose v6.6.203 for Linux & Windows - 漏洞扫描 Rapid7 Vulnerability Management, Release Jul 05, 2023 请访问原文链接&#xff1a;https://sysin.org/blog/nexpose-6/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.o…

【吴恩达】prompt engineering(原则 迭代 文本概括 推断、订餐机器人)

简介 Introduction 基础的LLM训练的模型&#xff0c;问法国的首都什么&#xff0c;可能会将答案预测为“法国最大的城市是什么&#xff0c;法国的人口是多少”许多 LLMs 的研究和实践的动力正在指令调整的 LLMs 上。指令调整的 LLMs 已经被训练来遵循指令。因此&#xff0c;如…