Java详解LeetCode 热题 100(22):LeetCode 160. 相交链表(Intersection of Two Linked Lists)详解

1. 题目描述

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

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

链表 A:      a1 → a2 ↘
                     c1 → c2 → c3
链表 B: b1 → b2 → b3 ↗

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

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

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null。

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 1 <= m, n <= 3 * 10^4
  • 1 <= Node.val <= 10^5
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listA 和 listB 没有交点,intersectVal 为 0
  • 如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]

进阶: 你能否设计一个时间复杂度 O(m + n)、仅用 O(1) 内存的解决方案?

2. 理解题目

这道题要求我们找到两个单链表的相交节点。关键理解点:

  1. 相交的定义:两个链表在某个节点开始共享相同的节点(不仅仅是值相同,而是同一个节点对象)
  2. 相交后的结构:一旦两个链表相交,从相交点开始,后续的所有节点都是共享的
  3. 返回要求:返回相交的起始节点,如果不相交则返回 null
  4. 结构保持:算法执行后,原链表结构不能被破坏

2.1 链表节点定义

// 链表节点的定义
public class ListNode {
    int val;
    ListNode next;
    
    ListNode(int x) {
        val = x;
    }
}

2.2 相交情况分析

两个链表可能的情况:

  1. 不相交:两个链表完全独立,没有共同节点
  2. 相交:两个链表在某个节点开始共享后续所有节点

相交的特点:

  • 相交节点之后的所有节点都是共享的
  • 两个链表的尾节点必须是同一个节点
  • 相交点可能在链表的任何位置

3. 解法一:哈希表法

3.1 思路

最直观的解法是使用哈希表:

  1. 遍历链表A,将所有节点存入哈希表
  2. 遍历链表B,检查每个节点是否在哈希表中
  3. 第一个在哈希表中找到的节点就是相交节点

3.2 Java代码实现

import java.util.HashSet;
import java.util.Set;

class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 处理边界情况
        if (headA == null || headB == null) {
            return null;
        }
        
        // 使用HashSet存储链表A的所有节点
        Set<ListNode> nodesInA = new HashSet<>();
        
        // 遍历链表A,将所有节点加入HashSet
        ListNode current = headA;
        while (current != null) {
            nodesInA.add(current);
            current = current.next;
        }
        
        // 遍历链表B,检查每个节点是否在HashSet中
        current = headB;
        while (current != null) {
            // 如果当前节点在HashSet中,说明找到了相交节点
            if (nodesInA.contains(current)) {
                return current;
            }
            current = current.next;
        }
        
        // 没有找到相交节点
        return null;
    }
}

3.3 代码详解

让我们详细分析每一步:

// 处理边界情况
if (headA == null || headB == null) {
    return null;
}
  • 如果任一链表为空,不可能有相交节点,直接返回null
// 使用HashSet存储链表A的所有节点
Set<ListNode> nodesInA = new HashSet<>();

// 遍历链表A,将所有节点加入HashSet
ListNode current = headA;
while (current != null) {
    nodesInA.add(current);
    current = current.next;
}
  • 创建HashSet存储链表A的所有节点
  • 遍历链表A,将每个节点(不是节点的值,而是节点对象本身)加入HashSet
  • 这样我们就记录了链表A中的所有节点
// 遍历链表B,检查每个节点是否在HashSet中
current = headB;
while (current != null) {
    // 如果当前节点在HashSet中,说明找到了相交节点
    if (nodesInA.contains(current)) {
        return current;
    }
    current = current.next;
}
  • 遍历链表B的每个节点
  • 检查当前节点是否在HashSet中
  • 如果找到,说明这个节点既在链表A中又在链表B中,即为相交节点
  • 返回第一个找到的相交节点

3.4 复杂度分析

  • 时间复杂度: O(m + n),其中m和n分别是两个链表的长度。需要遍历两个链表各一次。
  • 空间复杂度: O(m),需要存储链表A的所有节点。

3.5 适用场景

哈希表法是最直观的解法,容易理解和实现,适合:

  • 对空间复杂度要求不严格的场景
  • 需要快速实现的情况
  • 作为其他复杂解法的对比基准

4. 解法二:双指针法(推荐)

4.1 思路

双指针法是最优雅的解法,核心思想是:

  1. 使用两个指针分别从两个链表的头部开始遍历
  2. 当指针到达链表末尾时,跳转到另一个链表的头部继续遍历
  3. 如果两个链表相交,两个指针最终会在相交节点相遇
  4. 如果不相交,两个指针最终都会变为null

关键洞察

  • 设链表A长度为m,链表B长度为n
  • 设相交前A的长度为a,相交前B的长度为b,相交部分长度为c
  • 则:m = a + c,n = b + c
  • 指针pA走过的路径:a + c + b
  • 指针pB走过的路径:b + c + a
  • 两个指针走过的总长度相同:a + c + b = b + c + a

4.2 Java代码实现

class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 处理边界情况
        if (headA == null || headB == null) {
            return null;
        }
        
        // 初始化两个指针
        ListNode pA = headA;
        ListNode pB = headB;
        
        // 当两个指针不相等时继续遍历
        while (pA != pB) {
            // 如果pA到达链表A的末尾,跳转到链表B的头部
            // 否则移动到下一个节点
            pA = (pA == null) ? headB : pA.next;
            
            // 如果pB到达链表B的末尾,跳转到链表A的头部
            // 否则移动到下一个节点
            pB = (pB == null) ? headA : pB.next;
        }
        
        // 返回相交节点(如果不相交,pA和pB都为null)
        return pA;
    }
}

4.3 代码详解

让我们详细分析这个巧妙的算法:

// 处理边界情况
if (headA == null || headB == null) {
    return null;
}
  • 如果任一链表为空,不可能相交
// 初始化两个指针
ListNode pA = headA;
ListNode pB = headB;
  • pA从链表A的头部开始
  • pB从链表B的头部开始
// 当两个指针不相等时继续遍历
while (pA != pB) {
    // 如果pA到达链表A的末尾,跳转到链表B的头部
    // 否则移动到下一个节点
    pA = (pA == null) ? headB : pA.next;
    
    // 如果pB到达链表B的末尾,跳转到链表A的头部
    // 否则移动到下一个节点
    pB = (pB == null) ? headA : pB.next;
}

这是算法的核心部分:

  • 当pA到达链表A末尾(pA.next为null,pA变为null)时,让pA跳转到链表B的头部
  • 当pB到达链表B末尾时,让pB跳转到链表A的头部
  • 这样确保两个指针走过的总路径长度相同
// 返回相交节点(如果不相交,pA和pB都为null)
return pA;
  • 如果相交,pA和pB会在相交节点相遇
  • 如果不相交,pA和pB最终都会变为null,此时pA == pB == null

4.4 算法执行过程示例

让我们通过示例1来跟踪算法执行:

链表A: [4,1,8,4,5],链表B: [5,6,1,8,4,5],相交于节点8

初始状态:
pA -> 4 (链表A)
pB -> 5 (链表B)

第1步:pA != pB
pA -> 1, pB -> 6

第2步:pA != pB  
pA -> 8, pB -> 1

第3步:pA != pB
pA -> 4, pB -> 8

第4步:pA != pB
pA -> 5, pB -> 4

第5步:pA != pB
pA -> null (到达A末尾), pB -> 5

第6步:pA != pB
pA -> 5 (跳转到B头部), pB -> null (到达B末尾)

第7步:pA != pB
pA -> 6, pB -> 4 (跳转到A头部)

第8步:pA != pB
pA -> 1, pB -> 1

第9步:pA != pB
pA -> 8, pB -> 8

第10步:pA == pB (都指向相交节点8)
返回节点8

4.5 复杂度分析

  • 时间复杂度: O(m + n),每个指针最多遍历两个链表一次。
  • 空间复杂度: O(1),只使用了两个指针变量。

4.6 为什么这个算法有效?

数学证明:

  • 设链表A长度为m,链表B长度为n
  • 如果相交,设相交前A的独有部分长度为a,B的独有部分长度为b,共同部分长度为c
  • 则:m = a + c,n = b + c
  • pA的路径:a + c + b = m + b
  • pB的路径:b + c + a = n + a
  • 当pA走了m+b步,pB走了n+a步时:
    • pA位置:从B头部走了b步,到达相交点
    • pB位置:从A头部走了a步,到达相交点
    • 此时pA == pB,算法结束

如果不相交:

  • pA路径:m + n步后到达null
  • pB路径:n + m步后到达null
  • 此时pA == pB == null,算法结束

5. 解法三:长度差法

5.1 思路

另一种直观的解法是先计算两个链表的长度差:

  1. 分别计算两个链表的长度
  2. 让较长链表的指针先走长度差步
  3. 然后两个指针同时移动,直到相遇或到达末尾

5.2 Java代码实现

class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 处理边界情况
        if (headA == null || headB == null) {
            return null;
        }
        
        // 计算两个链表的长度
        int lenA = getLength(headA);
        int lenB = getLength(headB);
        
        // 初始化两个指针
        ListNode pA = headA;
        ListNode pB = headB;
        
        // 让较长链表的指针先走长度差步
        if (lenA > lenB) {
            // 链表A更长,pA先走 lenA - lenB 步
            for (int i = 0; i < lenA - lenB; i++) {
                pA = pA.next;
            }
        } else {
            // 链表B更长,pB先走 lenB - lenA 步
            for (int i = 0; i < lenB - lenA; i++) {
                pB = pB.next;
            }
        }
        
        // 两个指针同时移动,直到相遇或到达末尾
        while (pA != null && pB != null) {
            if (pA == pB) {
                return pA; // 找到相交节点
            }
            pA = pA.next;
            pB = pB.next;
        }
        
        // 没有相交节点
        return null;
    }
    
    // 辅助方法:计算链表长度
    private int getLength(ListNode head) {
        int length = 0;
        ListNode current = head;
        while (current != null) {
            length++;
            current = current.next;
        }
        return length;
    }
}

5.3 代码详解

// 计算两个链表的长度
int lenA = getLength(headA);
int lenB = getLength(headB);
  • 使用辅助方法计算两个链表的长度
// 让较长链表的指针先走长度差步
if (lenA > lenB) {
    // 链表A更长,pA先走 lenA - lenB 步
    for (int i = 0; i < lenA - lenB; i++) {
        pA = pA.next;
    }
} else {
    // 链表B更长,pB先走 lenB - lenA 步
    for (int i = 0; i < lenB - lenA; i++) {
        pB = pB.next;
    }
}
  • 计算长度差,让较长链表的指针先移动
  • 这样确保两个指针到链表末尾的距离相同
// 两个指针同时移动,直到相遇或到达末尾
while (pA != null && pB != null) {
    if (pA == pB) {
        return pA; // 找到相交节点
    }
    pA = pA.next;
    pB = pB.next;
}
  • 两个指针同步移动
  • 如果相交,它们会在相交节点相遇
  • 如果不相交,它们会同时到达末尾(null)

5.4 复杂度分析

  • 时间复杂度: O(m + n),需要遍历两个链表计算长度,然后再遍历一次找相交点。
  • 空间复杂度: O(1),只使用了常数个变量。

6. 解法四:栈法

6.1 思路

使用栈的特性(后进先出)来解决:

  1. 将两个链表的所有节点分别压入两个栈
  2. 同时从两个栈顶弹出节点进行比较
  3. 最后一个相同的节点就是相交节点

6.2 Java代码实现

import java.util.Stack;

class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 处理边界情况
        if (headA == null || headB == null) {
            return null;
        }
        
        // 创建两个栈
        Stack<ListNode> stackA = new Stack<>();
        Stack<ListNode> stackB = new Stack<>();
        
        // 将链表A的所有节点压入栈A
        ListNode current = headA;
        while (current != null) {
            stackA.push(current);
            current = current.next;
        }
        
        // 将链表B的所有节点压入栈B
        current = headB;
        while (current != null) {
            stackB.push(current);
            current = current.next;
        }
        
        // 从栈顶开始比较,找到最后一个相同的节点
        ListNode intersection = null;
        while (!stackA.isEmpty() && !stackB.isEmpty()) {
            ListNode nodeA = stackA.pop();
            ListNode nodeB = stackB.pop();
            
            if (nodeA == nodeB) {
                intersection = nodeA; // 更新相交节点
            } else {
                break; // 找到第一个不同的节点,停止
            }
        }
        
        return intersection;
    }
}

6.3 代码详解

// 将链表A的所有节点压入栈A
ListNode current = headA;
while (current != null) {
    stackA.push(current);
    current = current.next;
}
  • 遍历链表A,将所有节点压入栈A
  • 栈顶是链表A的最后一个节点
// 从栈顶开始比较,找到最后一个相同的节点
ListNode intersection = null;
while (!stackA.isEmpty() && !stackB.isEmpty()) {
    ListNode nodeA = stackA.pop();
    ListNode nodeB = stackB.pop();
    
    if (nodeA == nodeB) {
        intersection = nodeA; // 更新相交节点
    } else {
        break; // 找到第一个不同的节点,停止
    }
}
  • 从两个栈顶同时弹出节点进行比较
  • 如果相同,更新相交节点
  • 如果不同,说明已经超过了相交部分,停止比较

6.4 复杂度分析

  • 时间复杂度: O(m + n),需要遍历两个链表各一次。
  • 空间复杂度: O(m + n),需要两个栈存储所有节点。

7. 详细步骤分析与示例跟踪

让我们通过具体示例详细跟踪双指针法的执行过程。

7.1 示例1:有相交的情况

输入:

  • 链表A: [4,1,8,4,5]
  • 链表B: [5,6,1,8,4,5]
  • 相交于值为8的节点

执行过程:

初始状态:
链表A: 4 -> 1 -> 8 -> 4 -> 5 -> null
链表B: 5 -> 6 -> 1 -> 8 -> 4 -> 5 -> null
pA指向4, pB指向5

步骤1: pA=4, pB=5 (不相等)
pA移动到1, pB移动到6

步骤2: pA=1, pB=6 (不相等)  
pA移动到8, pB移动到1

步骤3: pA=8, pB=1 (不相等)
pA移动到4, pB移动到8

步骤4: pA=4, pB=8 (不相等)
pA移动到5, pB移动到4

步骤5: pA=5, pB=4 (不相等)
pA移动到null, pB移动到5

步骤6: pA=null, pB=5 (不相等)
pA跳转到链表B头部(5), pB移动到null

步骤7: pA=5, pB=null (不相等)
pA移动到6, pB跳转到链表A头部(4)

步骤8: pA=6, pB=4 (不相等)
pA移动到1, pB移动到1

步骤9: pA=1, pB=1 (不相等,但值相同,注意这里比较的是节点对象)
pA移动到8, pB移动到8

步骤10: pA=8, pB=8 (相等!找到相交节点)
返回节点8

7.2 示例2:无相交的情况

输入:

  • 链表A: [2,6,4]
  • 链表B: [1,5]
  • 无相交

执行过程:

初始状态:
链表A: 2 -> 6 -> 4 -> null
链表B: 1 -> 5 -> null
pA指向2, pB指向1

步骤1: pA=2, pB=1 (不相等)
pA移动到6, pB移动到5

步骤2: pA=6, pB=5 (不相等)
pA移动到4, pB移动到null

步骤3: pA=4, pB=null (不相等)
pA移动到null, pB跳转到链表A头部(2)

步骤4: pA=null, pB=2 (不相等)
pA跳转到链表B头部(1), pB移动到6

步骤5: pA=1, pB=6 (不相等)
pA移动到5, pB移动到4

步骤6: pA=5, pB=4 (不相等)
pA移动到null, pB移动到null

步骤7: pA=null, pB=null (相等!)
返回null(无相交)

8. 常见错误与优化

8.1 常见错误

  1. 比较节点值而不是节点对象

    // 错误:比较节点的值
    if (pA.val == pB.val) {
        return pA;
    }
    
    // 正确:比较节点对象
    if (pA == pB) {
        return pA;
    }
    
  2. 忘记处理边界情况

    // 错误:没有检查空链表
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode pA = headA;
        // ...
    }
    
    // 正确:先检查边界情况
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        // ...
    }
    
  3. 双指针法中的跳转逻辑错误

    // 错误:跳转条件不正确
    pA = (pA.next == null) ? headB : pA.next;
    
    // 正确:当pA为null时才跳转
    pA = (pA == null) ? headB : pA.next;
    
  4. 栈法中忘记更新相交节点

    // 错误:找到第一个相同节点就返回
    if (nodeA == nodeB) {
        return nodeA;
    }
    
    // 正确:继续比较,找到最早的相交节点
    if (nodeA == nodeB) {
        intersection = nodeA;
    } else {
        break;
    }
    

8.2 性能优化

  1. 提前终止优化

    // 优化:如果两个链表的尾节点不同,肯定不相交
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) {
            return null;
        }
        
        // 找到两个链表的尾节点
        ListNode tailA = headA;
        while (tailA.next != null) {
            tailA = tailA.next;
        }
        
        ListNode tailB = headB;
        while (tailB.next != null) {
            tailB = tailB.next;
        }
        
        // 如果尾节点不同,肯定不相交
        if (tailA != tailB) {
            return null;
        }
        
        // 继续使用双指针法
        // ...
    }
    
  2. 减少重复计算

    // 在长度差法中,可以在计算长度的同时记录尾节点
    private int getLengthAndTail(ListNode head, ListNode[] tail) {
        int length = 0;
        ListNode current = head;
        while (current != null) {
            length++;
            if (current.next == null) {
                tail[0] = current; // 记录尾节点
            }
            current = current.next;
        }
        return length;
    }
    

9. 扩展题目与应用

9.1 相关题目

  1. LeetCode 141. 环形链表
    判断链表中是否有环,也可以使用双指针法(快慢指针)

  2. LeetCode 142. 环形链表 II
    找到环形链表中环的起始节点

  3. LeetCode 19. 删除链表的倒数第N个节点
    使用双指针法,一个指针先走N步

9.2 实际应用场景

  1. 内存管理

    • 检测对象引用的共享
    • 垃圾回收中的引用分析
  2. 数据结构设计

    • 实现共享数据结构
    • 优化内存使用
  3. 图算法

    • 检测图中的公共路径
    • 寻找最短公共祖先
  4. 版本控制系统

    • Git中的分支合并点检测
    • 代码历史的公共提交点

10. 完整的 Java 解决方案

以下是推荐的双指针法完整实现,包含详细注释:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *     }
 * }
 */
public class Solution {
    /**
     * 找到两个单链表的相交节点
     * 使用双指针法,时间复杂度O(m+n),空间复杂度O(1)
     * 
     * @param headA 链表A的头节点
     * @param headB 链表B的头节点
     * @return 相交节点,如果不相交返回null
     */
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 边界情况:如果任一链表为空,不可能相交
        if (headA == null || headB == null) {
            return null;
        }
        
        // 初始化两个指针,分别指向两个链表的头部
        ListNode pA = headA;
        ListNode pB = headB;
        
        // 核心思想:让两个指针走过相同的路径长度
        // pA路径:链表A + 链表B
        // pB路径:链表B + 链表A
        // 如果相交,两个指针会在相交点相遇
        // 如果不相交,两个指针最终都会变为null
        while (pA != pB) {
            // 当pA到达链表A末尾时,跳转到链表B头部
            // 否则移动到下一个节点
            pA = (pA == null) ? headB : pA.next;
            
            // 当pB到达链表B末尾时,跳转到链表A头部  
            // 否则移动到下一个节点
            pB = (pB == null) ? headA : pB.next;
        }
        
        // 返回相交节点(如果不相交,pA和pB都为null)
        return pA;
    }
}

10.1 测试用例

public class TestIntersection {
    public static void main(String[] args) {
        Solution solution = new Solution();
        
        // 测试用例1:有相交的情况
        // 创建链表A: 4 -> 1 -> 8 -> 4 -> 5
        ListNode a1 = new ListNode(4);
        ListNode a2 = new ListNode(1);
        ListNode c1 = new ListNode(8); // 相交节点
        ListNode c2 = new ListNode(4);
        ListNode c3 = new ListNode(5);
        
        a1.next = a2;
        a2.next = c1;
        c1.next = c2;
        c2.next = c3;
        
        // 创建链表B: 5 -> 6 -> 1 -> 8 -> 4 -> 5
        ListNode b1 = new ListNode(5);
        ListNode b2 = new ListNode(6);
        ListNode b3 = new ListNode(1);
        
        b1.next = b2;
        b2.next = b3;
        b3.next = c1; // 指向相交节点
        
        ListNode result1 = solution.getIntersectionNode(a1, b1);
        System.out.println("测试用例1结果: " + (result1 != null ? result1.val : "null"));
        // 期望输出: 8
        
        // 测试用例2:无相交的情况
        ListNode d1 = new ListNode(2);
        ListNode d2 = new ListNode(6);
        ListNode d3 = new ListNode(4);
        
        ListNode e1 = new ListNode(1);
        ListNode e2 = new ListNode(5);
        
        d1.next = d2;
        d2.next = d3;
        e1.next = e2;
        
        ListNode result2 = solution.getIntersectionNode(d1, e1);
        System.out.println("测试用例2结果: " + (result2 != null ? result2.val : "null"));
        // 期望输出: null
    }
}

11. 算法比较与选择

11.1 各解法对比

解法时间复杂度空间复杂度优点缺点
哈希表法O(m+n)O(m)直观易懂,实现简单需要额外空间
双指针法O(m+n)O(1)空间最优,代码简洁理解稍难
长度差法O(m+n)O(1)逻辑清晰,易于理解代码稍长
栈法O(m+n)O(m+n)思路直观空间消耗大

11.2 推荐选择

  1. 面试推荐:双指针法

    • 时间和空间复杂度都是最优的
    • 代码简洁优雅
    • 体现了算法思维
  2. 学习理解:长度差法

    • 逻辑最直观
    • 容易理解和记忆
    • 适合初学者
  3. 快速实现:哈希表法

    • 最容易想到和实现
    • 不容易出错
    • 适合时间紧张的情况

12. 总结与技巧

12.1 解题要点

  1. 理解相交的定义:是节点对象相同,不是节点值相同
  2. 掌握双指针技巧:让两个指针走过相同的路径长度
  3. 处理边界情况:空链表、单节点链表等
  4. 选择合适的解法:根据时间和空间要求选择最优解法

12.2 学习收获

通过学习相交链表问题,你可以掌握:

  • 链表操作的基本技巧
  • 双指针法的巧妙应用
  • 空间和时间复杂度的权衡
  • 多种解法的对比分析

12.3 面试技巧

如果在面试中遇到此类问题:

  1. 先确认题目要求(是节点相交还是值相交)
  2. 分析不同解法的优缺点
  3. 选择最优解法并解释原理
  4. 考虑边界情况和错误处理
  5. 分析时间和空间复杂度

12.4 记忆技巧

双指针法的核心思想可以这样记忆:

  • “你走过我走过的路,我走过你走过的路”
  • “如果有缘分(相交),我们终会相遇”
  • “如果没有缘分(不相交),我们都会走到尽头(null)”

13. 参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈凯哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值