【码道初阶】【LeetCode 160】相交链表:让跑者“起跑线对齐”的智慧

【LeetCode 160】相交链表:让跑者"起跑线对齐"的智慧

在链表问题中,"相交链表"(Intersection of Two Linked Lists)是一道考察指针操作和空间思维的经典题目。

题目要求我们找到两个单链表的交点。难点在于:两个链表的长度可能不一样

就像两个从不同岔路口出发的跑者,如果我们要他们在岔路汇合点相遇,必须解决"路程差"的问题。

今天我们就来拆解一种最直观、最稳健的解法:长度差对齐法

1. 核心思路:消除"贫富差距"

假设链表 A 长 5 米,链表 B 长 3 米,它们在倒数第 2 米处相交。

如果我们同时从头开始遍历,A 还在前半截跑的时候,B 可能已经跑完或者跑过了,两人根本碰不到面。

解法直觉

既然 A 比 B 长,那我就算出 A 比 B 长多少(比如长 k),先让 A 提前跑 k 步。此时,A 和 B 距离终点(或交点)的距离就一样了。接下来两人齐头并进,一定会在交点相遇。

2. 代码深度拆解

这个代码完美地贯彻了这个思路,我们将其分为三个阶段:

第一阶段:量尺寸(计算长度)

首先,我们需要知道两条链表各自有多长,从而计算差值。

java 复制代码
ListNode list1 = headA;
ListNode list2 = headB;
int lenA = 0;
int lenB = 0;

// 遍历 A 获取长度
while(list1 != null) {
    list1 = list1.next;
    lenA++;
}
// 遍历 B 获取长度
while(list2 != null) {
    list2 = list2.next;
    lenB++;
}

第二阶段:找差距 & 换跑道(关键逻辑)

这一步是代码的精华。我们需要确定谁长谁短,并计算差值 len

为了简化后续逻辑,我们通常强制让 list1 指向较长的那个链表

java 复制代码
int len = lenA - lenB;

// 【重中之重】刚才遍历完 list1 和 list2 都跑到 null 了,必须拉回起点!
list1 = headA;
list2 = headB; 

// 如果 len < 0,说明 B 比 A 长
// 我们交换一下,让 list1 代表长链表,list2 代表短链表
if(len < 0) {
    list1 = headB;
    list2 = headA;
    len = lenB - lenA; // 修正差值为正数
}

代码亮点

  • 状态重置list1 = headA; 这一步非常容易忘!很多新手算完长度直接就往下写,结果报空指针,因为此时指针还在链表尾部。
  • 动态交换 :通过 if(len < 0) 的判断和交换,保证了接下来的代码不需要写两套逻辑,永远只需要操作 list1 先走

第三阶段:让长链表先走 & 齐头并进

现在 list1 是长链表,len 是长度差。

java 复制代码
// 1. 长链表先走 len 步
while(len != 0) {
    list1 = list1.next;
    len--;
}

// 2. 此时 list1 和 list2 站在了同一起跑线(距离末尾长度相同)
//    一起往后走,直到相遇
while(list1 != list2) {
    list1 = list1.next;
    list2 = list2.next;
}

// 3. 返回结果
//    如果相交,list1 就是交点;
//    如果不相交,最后 list1 和 list2 都会变成 null,循环结束,返回 null。
return list1;

3. ⚠️ 易错点与避坑指南

结合这段代码,有几个面试或刷题时容易翻车的地方,需要重点标记:

易错点 1:指针复位(Reset State)

在计算完长度后,list1list2 已经指向了链表的末尾(null)。
必须手动将它们重新指向 headAheadB

你的代码中专门注释了 //记得重置状态!,这是一个非常好的编程习惯。

易错点 2:比较的是节点对象,不是值

题目中强调了:

请注意相交节点的值不为 1... 它们在内存中指向两个不同的位置。

在判断相交时,必须使用 list1 != list2(比较内存地址/引用)。
绝对不能 写成 list1.val != list2.val。因为两条不同的链表可能有数值相同的节点,但它们并不是同一个物理节点(交点)。

易错点 3:如果不相交怎么办?

逻辑上不需要特殊处理。

如果不相交,两个指针会并排走到最后,同时变成 null
while(list1 != list2) 会因为 null == null 而跳出循环,直接返回 null,符合题目要求。

4. 复杂度分析

  • 时间复杂度 :O(N+M)O(N + M)O(N+M)。
    • 我们需要遍历 A 和 B 各一次来算长度。
    • 然后再遍历一次来找交点。
    • 总操作次数约为 2N+2M2N + 2M2N+2M,属于线性时间复杂度。
  • 空间复杂度 :O(1)O(1)O(1)。
    • 我们只用到了几个指针变量(list1, list2, len),没有申请额外的数组或哈希表。

5. 总结

这道题的解法体现了**"预处理"**的思想。

直接做很难(因为不知道交点在哪里),但如果我们通过预先计算长度,消除了两个链表的差异,问题就退化成了简单的"同步遍历"。

代码简洁、逻辑清晰且没有复杂的边界条件,这就是长度差对齐法的魅力所在。


相关推荐
大厂技术总监下海6 分钟前
数据湖加速、实时数仓、统一查询层:Apache Doris 如何成为现代数据架构的“高性能中枢”?
大数据·数据库·算法·apache
UIUI8 分钟前
list_for_each_entry
linux·数据结构·链表
a程序小傲9 分钟前
小红书Java面试被问:TCC事务的悬挂、空回滚问题解决方案
java·开发语言·人工智能·后端·python·面试·职场和发展
短剑重铸之日18 分钟前
《SpringBoot4.0初识》第五篇:实战代码
java·后端·spring·springboot4.0
hetao173383722 分钟前
2026-01-06 hetao1733837 的刷题笔记
c++·笔记·算法
heartbeat..22 分钟前
Spring MVC 全面详解(Java 主流 Web 开发框架)
java·网络·spring·mvc·web
-西门吹雪24 分钟前
c++线程之std::async浅析
java·jvm·c++
a努力。35 分钟前
国家电网Java面试被问:最小生成树的Kruskal和Prim算法
java·后端·算法·postgresql·面试·linq
朝九晚五ฺ37 分钟前
从零到实战:鲲鹏平台 HPC 技术栈与并行计算
java·开发语言
CUIYD_198939 分钟前
Freemarker 无法转译 & 字符
java·开发语言·spring