七、链表问题(上)

160、相交链表(简单)

题目描述

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

图示两个链表在节点 c1 开始相交:

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

示例1:

示例2:

示例3:

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 1 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105

题目思路

对于这道题,是非常经典的链表类型的题目,要求获得两个链表交叉的位置(如果不交叉返回为空)。

需要注意的是,两个单链表的交叉,并不是像 “染色体” 那种X形式的交叉,因为一个单链表节点只能有一个后继(next),因此,只要交叉,就相当于二者汇融在了一起。

首先最直接的方法,是先获得两个链表的长度,然后让其中一个长度长的先走(len_a - len_b)的距离,最后,二者指针再一起走,就可以获得相交叉的节点了。

同时还有第二种不需要计算链表长度的方法:

  • 创建指针 ab ,分别指向headAheadB
  • 每步操作同时更新 ab
    • 如果指针 a 不为空,则将指针 a 移到下一个节点;如果指针 b 不为空,则将指针 b 移到下一个节点;
    • 如果指针 a 为空,则将指针 a 移到链表 headB 的头节点;如果指针 b 为空,则将指针 b 移到链表 headA 的头节点;
  • 当指针 ab 指向同一个节点或者都为空时,返回它们指向的节点或者 None

    通过两个指针,第一轮让二者到达末尾的节点然后指向对方的头部,这样就相当于是抹除了距离差。
    第二轮的行走,如果有交点,这二者一定会相交,否则二者会一同走到结尾的None

算法代码

1、计算链表长度

class Solution:def getListLength(self, head):res = 0while head:res += 1head = head.nextreturn resdef getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:if headA is None or headB is None:return Nonelen_a, len_b = self.getListLength(headA), self.getListLength(headB)if len_a >= len_b:gap = len_a - len_bwhile gap > 0:headA = headA.nextgap -= 1else:gap = len_b - len_awhile gap > 0:headB =headB.nextgap -= 1while headA != headB:headA = headA.nextheadB = headB.nextreturn headA

2、双指针法

class Solution:def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:if headA is None or headB is None:return Nonea, b = headA, headBwhile a != b:a = a.next if a else headBb = b.next if b else headAreturn a

206、反转链表(简单)

题目描述

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例1:

示例2:

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

题目思路

对于这道题,也是链表问题中较为经典的类型——翻转。
题目的标准解法,就是通过定义一个prev节点来记录链表翻转后应当指向的节点:

  • 记录当前节点head的下一个节点next(因为一旦翻转而不记录,原始next将会丢失)
  • head节点指向所记录的上一个节点prev
  • prev节点指向当前的head节点,作为下一轮循环的上一个节点;
  • head节点指向第一步记录的next节点,作为下一轮循环的当前节点;

算法代码

class Solution:def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:prev_node = Nonewhile head:next_node = head.nexthead.next = prev_nodeprev_node = headhead = next_nodereturn prev_node

234、回文链表(简单)

题目描述

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false

示例1:

输入:head = [1,2,2,1]
输出:true

示例 2:

输入:head = [1,2]
输出:false

提示:

  • 链表中节点数目在范围[1, 105] 内
  • 0 <= Node.val <= 9

进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

题目思路

对于这道题,要求我们判断一个链表是否构成回文形式。
所谓回文,就是指1, 2, 2, 1这种形式,对于字符串或者数组而言,判断是否是回文相对简单,直接从两端向中间遍历即可。

但是在这道题中,所给定的数据结构为单链表,这就让直接比那
这里我们有两种思路:

1、辅助数组
直接定义一个数组,并在遍历链表的过程中将链其转化为数组、判断回文。
这种方法虽然简答, 但需要额外开辟O(n)的空间。

2、快慢指针+翻转链表

  • 利用快慢指针法,找到链表中间位置;
  • 对后半段链表进行翻转,然后分别从原链表head与翻转后后半段链表头遍历、判断是否相等即可;

在这种方法中,不需要额外的列表进行存储,但有几点需要注意:

  • 快慢指针法找中间位置时,链表节点个数偶数还是奇数需要单独判断:
    • 对于奇数个节点的情况,slow还需再向后一位越过中位线;
  • 后半段链表翻转后,注意前半段最后一个节点指向的还是后半段当前第一个节点,以此要以后半段作为遍历和判空条件;

算法代码

1、辅助数组

	class Solution:def isPalindrome(self, head: Optional[ListNode]) -> bool:if head.next is None:return Truelist_val = []while head:list_val.append(head.val)head = head.nextn = len(list_val)for i in range(0, int(n/2)):if list_val[i] != list_val[n-1-i]:return Falsereturn True

2、快慢指针+翻转链表

	class Solution:def isPalindrome(self, head: Optional[ListNode]) -> bool:slow, fast = head, head# 使用快慢指针法找到链表中间位置# 注意链表节点个数偶数还是奇数需要单独判断while fast and fast.next:slow = slow.nextfast = fast.next.next# 对于奇数个节点的情况,slow还需再向后一位越过中位线if fast:slow = slow.next# fast指向head,slow翻转,就可以通过直接比较的方法实现链表回文判断fast = headslow = self._reverse_list(slow)# 这里注意需要使用slow作为判断标准# 因为后半部分翻转不意味着前半部分末尾指向Nonewhile slow:if slow.val != fast.val:return Falseslow = slow.nextfast = fast.nextreturn Truedef _reverse_list(self, head):prev_node = Nonewhile head:next_node = head.nexthead.next = prev_nodeprev_node = headhead = next_nodereturn prev_node

141、环形链表(简单)

题目描述

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

实例1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

实例2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

实例3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

进阶:
你能用 O(1)(即,常量)内存解决此问题吗?

题目思路

对于这道题,是链表类问题中较为典型的一种,需要我们判断一个链表中是否有环。

对于这类判断链表是否有环问题,我们一般使用快慢指针法,即定义两个指针,一个为slow,一个为fast,从头开始对链表进行扫描:

  • 指针slow每次走一步,指针fast每次走两步
  • 如果存在环,则这两个指针一定会相遇
  • 如果不存在环,则fast指针会最先到达NULL而退出。

为何说二者一定相遇?
因为fast会先进入环,在slow进入之后,如果把slow看做在前面,则fast在后面每次循环都会想slow靠近1,所以一定会相遇,而不会出现fast直接跳过slow的情况。

而另一类解法,则是直接对链表值做修改特殊值
如果我们遍历了一个节点,那么我们就将其的值修改为特殊的——比如无穷大INF,这样我们一直遍历下去,如果再次找到了这样的值为INF的节点,那么我们就可以认为这个链表是有环的。

算法代码

1、快慢指针法

class Solution:def hasCycle(self, head: Optional[ListNode]) -> bool:slow, fast = head, headwhile fast and fast.next:slow = slow.nextfast = fast.next.nextif slow == fast:return Truereturn False

2、修改链表值

class Solution:def hasCycle(self, head: ListNode) -> bool:INF = float('inf')while head != None:if head.val != INF:head.val = INFhead = head.nextelse:return Truereturn False

142、环形链表 II(中等)

题目描述

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104] 内
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

进阶:

  • 你是否可以使用 O(1) 空间解决此题?

题目思路

对于这道题,是在上一个判断链表是否有环的基础上更进一步、找到链表开始入环的第一个节点。

这里,我们同样使用快慢指针的方法,不过这里有一些比较巧妙的论证与计算。

首先,对于我们定义的快慢指针fastslow,在本题的求解过程中,双指针会产生两次“相遇”。

第一次相遇:

  • 设置指针fastslow,指向链表头部;
  • 每轮fast走2部,slow走一步;

如果链表存在环,那么双指针一定会相遇。因为每走 1 轮,fastslow 的间距 +1,fast 一定会追上 slow

设两指针分别走了FS的距离,因为fast走的步数是slow的两倍,因此我们有第一个关键公式:

  • F = 2 * S

其次,如下图所示,我们设定:

  • 第一次相遇的点为Pos
  • 环的连接点(即题目希望求解的位置)为Join
    • 该位置设为环的起始位置,只要绕的圈数为整数圈,那么一定落在Join点;
  • 头结点head到连接点Join的长度的为LenA;
  • 连接点Join到第一次相遇点Pos的长度为x
  • 环的长度为R;

    第一次相遇时,我们有:
  • slow走的长度为:S = LenA + x
  • fast走的长度为:F = LenA + n * R + x
    • 这里n * R是因为在slow进入到环前,fast可能已经在环内走了n圈了;

根据第一个公式F = 2 * S,因此我们有:

  • LenA = n * R - x

由于此时slowJoin点已经走了x距离,那么只需要再走R - x的距离即可到达Join点。
因此,假如此时在第一次相遇后,我们让fast重新指向headfastslow同时每轮一步、走LenA的距离,那么此时slow行走后所在位置为:

  • x + LenA = x + n * R - x = n * R

n * R恰好正是Join点所在的位置。

这个位置也恰好就是fastslow第二次相遇的位置。

补充:
如果觉得n * R不好理解,将LenA设置较短、n变为0即可。

算法代码

class Solution:def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:has_cycle = Falseslow, fast = head, headwhile fast and fast.next:slow = slow.nextfast = fast.next.nextif slow == fast:has_cycle = Truebreakif not has_cycle:return Nonefast = headwhile fast != slow:fast = fast.nextslow = slow.nextreturn fast

21、合并两个有序列表(简单)

题目描述

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

题目思路

对于这道题,整体较为简单,就是对两个链表同时进行遍历、不断修改next、最终达到重新组织排序的目的。

其中有几个注意点:

  • 需要新创建一个pre_node用于记录链表开头部分;
  • 定义p节点用于遍历、记录当前节点;
  • 最后只要list1list2还剩下元素,直接将其放在p后面即可;

算法代码

class Solution:def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:prev_head = ListNode(-1)p = pre_headwhile list1 and list2:if list1.val <= list2.val:p.next = list1list1 = list1.nextelse:p.next = list2list2 = list2.nextp = p.next# 将剩下的链表直接放在结尾p.next = list1 if list1 else list2return prev_head.next

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

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

相关文章

如何选择阿里云服务器配置?(CPU/内存/带宽/磁盘)

阿里云服务器配置怎么选择&#xff1f;CPU内存、公网带宽和系统盘怎么选择&#xff1f;个人开发者或中小企业选择轻量应用服务器、ECS经济型e实例&#xff0c;企业用户选择ECS通用算力型u1云服务器、ECS计算型c7、通用型g7云服务器&#xff0c;阿里云服务器网aliyunfuwuqi.com整…

对象得定义与使用(动力节点老杜)

对象思想 1.什么是面向过程&#xff0c;什么是面向对象&#xff1f; 换而言之&#xff0c;面向对象思想实际就是将整体分成一个个独立的单元&#xff0c;每个单元都有自己得任务和属性&#xff0c;所有单元结合在一起完成一个整体。如果某个单元出现了问题还可以及时处理&…

比肩Gen-2,全新开源文生视频模型

著名开源平台Stability.ai在官网宣布&#xff0c;推出全新文生视频的扩散模型Stable Video Diffusion&#xff0c;已开源了该项目并公布了论文。 据悉&#xff0c;用户通过文本或图像就能生成高精准&#xff0c;14帧和25帧的短视频。目前&#xff0c;Stable Video Diffusion处…

回溯算法02-组合总合III(Java)

2.组合总合III 题目描述 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输入: k 3,…

[Linux]如何理解kernel、shell、bash

文章目录 概念总览kernelshell&bash 概念总览 内核(kernel) &#xff0c;外壳(shell) &#xff0c;bash kernel kernel是指操作系统中的核心部分&#xff0c;用户一般是不能直接使用kernel的。它主要负责管理硬件资源和提供系统服务&#xff0c;如内存管理、进程管理、文件…

Pytest测试技巧之Fixture:模块化管理测试数据!

在 Pytest 测试中&#xff0c;有效管理测试数据是提高测试质量和可维护性的关键。本文将深入探讨 Pytest 中的 Fixture&#xff0c;特别是如何利用 Fixture 实现测试数据的模块化管理&#xff0c;以提高测试用例的清晰度和可复用性。 什么是Fixture&#xff1f; 在 Pytest 中…

【考研数学】武忠祥各阶段用书搭配+学习包

25考研数学全流程规划&#xff01;别等到二战了才知道这样学 本人属于基础很差相当于是零基础的考研党&#xff0c;经过一年备考成功上岸 中间花费了很多时间在考研数学备考信息检索上&#xff0c;写下这篇希望能帮助基础不好的学弟学妹们多节约一些时间复习&#xff01; 25…

【PowerMockito:编写单元测试过程中原方法没有注入的属性在跑单元测试时出现空指针】

出错场景 下面这一步报空指针&#xff0c;但是因为没有注入&#xff0c;在测试类中无法使用Mock 解决 在执行方法前&#xff0c;加入以下代码 MemberModifier.field(ResourceServiceImpl.class,"zero").set(resourceService,"0");

网页打开慢,这锅该谁背?

一、背景 工作中扯皮说不可避免且非常常见的事情. 开发与产品、开发和测试、前端和后端都会产生扯皮现象。今天要聊的一个问题就是前后端之间的扯皮问题。 网页打开太慢或者点击了某个按钮发现数据很久才显示出来&#xff0c;这个锅谁背? 做开发不能无凭据地胡乱甩锅, 我们…

电子电气架构——汽车以太网诊断路由汇总

电子电气架构——汽车以太网诊断路由汇总 我是穿拖鞋的汉子,魔都中坚持长期主义的工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 人们会在生活中不断攻击你。他们的主要武器是向你灌输对自己的怀疑:你的价值、你的能力、你的潜力。他们往往会将…

python 蓝桥杯填空题

文章目录 字母数判断列名&#xff08;进制问题&#xff09;特殊日期大乘积星期几 字母数 由于是填空题&#xff0c;那么寻找的话&#xff0c;就直接让每一个位置都是A,通过计算看看是不是结果大于2022即可 判断列名&#xff08;进制问题&#xff09; 这道题目&#xff0c;我们可…

视频号带货究竟怎么做?老阳分享的项目怎么样?

在当今社会&#xff0c;随着互联网的快速发展&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分。在这个背景下&#xff0c;视频号带货作为一种新兴的电商模式&#xff0c;逐渐崭露头角。许多人都想通过加入视频号带货行业来实现自己的财富自由。其中&#xff0c;老…

SPSS Statistics for Mac/Win:让数据说话,为决策赋能

在数字化时代&#xff0c;数据已成为决策的关键。为了从海量数据中提取有价值的信息&#xff0c;专业的统计分析软件不可或缺。IBM SPSS Statistics就是这样一款强大的工具&#xff0c;无论是Windows还是Mac用户&#xff0c;都能通过它轻松实现数据分析和决策支持。 一、SPSS …

从0到1入门C++编程——09 STL、string容器、vector容器、deque容器

文章目录 一、标准模板库STL二、容器算法迭代器应用1、遍历容器中整型数据2、遍历容器中自定义数据类型3、容器中嵌套容器 三、string容器1、构造函数2、赋值操作3、字符串拼接4、查找和替换5、字符串比较6、字符访问与存取7、插入和删除8、子串 四、vector容器1、构造函数2、赋…

游戏力:竞技游戏设计实战教程

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 游戏力&#xff1a;竞技游戏设计实战教程 引言…

GBU3010-ASEMI火牛适配器专用整流桥GBU3010

编辑&#xff1a;ll GBU3010-ASEMI火牛适配器专用整流桥GBU3010 型号&#xff1a;GBU3010 品牌&#xff1a;ASEMI 封装&#xff1a;GBU-4 最大重复峰值反向电压&#xff1a;1000V 最大正向平均整流电流(Vdss)&#xff1a;30A 功率(Pd)&#xff1a;大功率 芯片个数&…

前端运算符比较与计算中的类型转换,运算规则

题目&#xff1a; 下面表达式的值分别都是什么&#xff08;类型转换&#xff09; 0 0 0 2 true 2 false false false false 0 false undefined false null null undefined\t\r\n 0JS中的原始类型有哪些 原始值类型就是 存储的都是值&#xff0c;没有函数可以调用的。…

【C++】二叉树进阶面试题(下)

目录 6. 根据一棵树的前序遍历与中序遍历构造二叉树 题目 分析 代码 7. 根据一棵树的中序遍历与后序遍历构造二叉树 题目 分析 代码 8. 二叉树的前序遍历&#xff0c;非递归迭代实现 题目 分析 代码 9. 二叉树中序遍历 &#xff0c;非递归迭代实现 题目 分析 …

RLNNA-DBSCAN聚类

RLNNA-DBSCAN聚类 RLNNA算法&#xff08;基于强化学习的神经网络优化算法&#xff09;是一种性能较佳的优化算法。DBSCAN聚类算法&#xff08;密度聚类算法&#xff09;是一种基于密度的聚类算法&#xff0c;其主要思想是通过寻找样本点周围的密度可达关系来聚类数据。 使用RL…

自动化专家来谈谈Selenium中的时间等待

在 Selenium 中&#xff0c;时间等待指在测试用例中等待某个操作完成或某个事件发生的时间。Selenium 中提供了多种方式来进行时间等待&#xff0c;包括使用 ExpectedConditions 中的 presence_of_element_located 和 visibility_of_element_located 方法等待元素可见或不可见&…