Algorithm-List2

date
Oct 28, 2023
slug
algorithm-list2
status
Published
tags
algorithm
type
Post
https://twitter.com/tangdahe
summary
list opt

24 两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
notion image

双指针法

建议使用虚拟头结点,这样会方便很多,要不然每次针对头结点(没有前一个指针指向头结点),还要单独处理。
  1. 每次处理两个即,curr 、curr.Next
  1. prev 需要指向反转后的头部节点
  1. 两个节点内部需要调换顺序
  1. 两个节点中后者节点需要继续保持链表后续链接,不然遇到奇数节点后续就断了
notion image
 

递归法

相对于双指针法,逻辑清晰,代码量更少
  1. 需要注意最终的终止条件
  1. 当前阶段,需要向上层返回 next 节点作为上层的下个连结节点
  1. 当前阶段,当前节点需要连接递归下一层的头部节点
  1. 当前阶段,需要调换两个节点
notion image
 
 

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

notion image
 

双指针

  1. 假设要删除倒数第 2 个节点,我们需要保留其前置节点 3,这样方便我们将 second.next = 5
  1. 快慢指针之间的距离为 n,当快指针到最后的时候,慢指针恰好为目标节点的前置节点
  1. 有一种特例,加入需要删除的节点就是第一个节点,我们是找不到其前置节点,因此需要给原始链表添加一个虚拟头节点方便操作
notion image
 

递归

  1. 假设要删除倒数第 2 个节点,我们需要保留其前置节点 3,这样方便我们将 3.next = 5
  1. 所以我们从最底层开始往上递归,nil 为最底层,返回 int = 0,每往上递归一层 idx++
  1. 直到倒数第四层 3,底层递归回来 idx 为 2 符合传入的 n,测试直接处理 curr.next = curr.next.next 即可
  1. 有一种特例,加入需要删除的节点就是第一个节点,我们是找不到其前置节点,因此需要给原始链表添加一个虚拟头节点方便操作
notion image
 
 

160 相交链表

  1. A B 两个链表分别从其头节点开始遍历,pa pb 分别指向头节点
  1. 当 pa 遍历完 A 链表时,pa = headB。同理 pb = headA
  1. 再进行遍历,直到 pa = pb 位置
  1. 这里不用担心不存在相交节点的情况,因为假设不相交,最后 pa = pb = nil,依旧可以返回正确结果
 

141 环形链表

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

如何判断链表有环?

快慢指针:
可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
为什么fast 走两个节点,slow走一个节点,有环的话,一定会在环内相遇呢,而不是永远的错开呢
首先第一点:fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。
那么来看一下,为什么fast指针和slow指针一定会相遇呢?
可以画一个环,然后让 fast指针在任意一个节点开始追赶slow指针。
会发现最终都是这种情况, 如下图:
notion image
fast和slow各自再走一步, fast和slow就相遇了
这是因为fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。

142 环形链表2

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
notion image
这道题目,不仅考察对链表的操作,而且还需要一些数学运算。
主要考察两知识点:
  • 判断链表是否环:同上述题目
  • 如果有环,如何找到这个环的入口

如果有环,如何找到这个环的入口

此时已经可以判断链表是否有环了,那么接下来要找这个环的入口了。
假设从头结点到环形入口节点 的节点数为 x。 环形入口节点到 fast 指针与 slow 指针相遇节点 节点数为 y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:
notion image
那么相遇时: slow 指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast 指针在环内多走了 n 圈才遇到 slow 指针, (y+z)为 一圈内节点的个数 A。
因为 fast 指针是一步走两个节点,slow 指针一步走一个节点, 所以 fast 指针走过的节点数 = slow 指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
两边消掉一个(x+y): x + y = n (y + z)
因为要找环形的入口,那么要求的是 x,因为 x 表示 头结点到 环形入口节点的的距离。
所以要求 x ,将 x 单独放在左面:x = n (y + z) - y ,
再从 n(y+z) 中提出一个(y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于 1 的,因为 fast 指针至少要多走一圈才能相遇 slow 指针。
那么 n 如果大于 1 是什么情况呢,就是 fast 指针在环形转 n 圈之后才遇到 slow 指针。
其实这种情况和 n 为 1 的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了 (n-1) 圈,然后再遇到 index2,相遇点依然是环形的入口节点。
 

© Craig Hart 2021 - 2025