寻找两个链表的交点的多种方法(算法)

寻找两个链表交点的核心是"消除两链表长度差",常见方法分为空间复杂度O(1) 和空间复杂度O(n) 两类,以下是具体实现:

节点

代码实现:

ini 复制代码
class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
            next = null;
        }
    }

一、双指针法(最优,空间O(1),时间O(m+n))

原理

  • 利用"两指针遍历总长度相等"消除长度差:设链表A长度为m,链表B长度为n,指针1从A出发、指针2从B出发,遍历完当前链表后立即切换到另一链表头部,最终两指针会走m+n步------若有交点则在交点相遇,无交点则同时为null。

代码实现:

java 复制代码
//双链表有共同节点
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    if (headA == null || headB == null) {
        return null; // 如果任一链表为空,则没有交点
    }
    ListNode pointerA = headA;
    ListNode pointerB = headB;

    // 当两个指针不相等时,继续遍历
    while (pointerA != pointerB) {
        // 如果指针A到达链表末尾,则重定向到链表B的头部
        pointerA = (pointerA == null) ? headB : pointerA.next;
        // 如果指针B到达链表末尾,则重定向到链表A的头部
        pointerB = (pointerB == null) ? headA : pointerB.next;
    }
    // 当指针相等时,要么是交点,要么都是null(无交点)
    return pointerA;
}

二、长度差法(空间O(1),时间O(m+n))

原理

1.先分别计算两链表的长度lenA和lenB,求出长度差diff = |lenA - lenB|;

2.让"长链表指针"先提前走diff步,使两指针从"距离尾部等长"的位置开始同步遍历;

3.两指针首次相遇的节点即为交点,若遍历结束未相遇则无交点。

代码实现:

ini 复制代码
//利用链表长度差值
public ListNode getIntersectionNodeLength(ListNode headA, ListNode headB) {
    if (headA == null || headB == null) {
        return null; // 如果任一链表为空,则没有交点
    }
    // 计算链表A的长度
    int lenA = 0;
    ListNode currentA = headA;
    while (currentA != null) {
        lenA++;
        currentA = currentA.next;
    }
    // 计算链表B的长度
    int lenB = 0;
    ListNode currentB = headB;
    while (currentB != null) {
        lenB++;
        currentB = currentB.next;
    }
    // 计算长度差
    int diff = Math.abs(lenA - lenB);
    // 将较长链表的指针前进diff步
    if (lenA > lenB) {
        for (int i = 0; i < diff; i++) {
            headA = headA.next;
        }
    } else {
        for (int i = 0; i < diff; i++) {
            headB = headB.next;
        }
    }
    // 同时遍历两个链表,寻找交点
    while (headA != null && headB != null) {
        if (headA == headB) {
            return headA; // 找到交点
        }
        headA = headA.next;
        headB = headB.next;
    }
    return null; // 没有交点
}

三、哈希集合法(空间O(n),时间O(m+n))

原理

1.遍历其中一个链表(如A),将所有节点存入哈希集合(利用集合"不可重复"特性);

2.遍历另一个链表(如B),逐个检查节点是否在哈希集合中------首次存在的节点即为交点;

3.若遍历结束未找到,则无交点。

代码实现:

csharp 复制代码
//哈希表法
public ListNode getIntersectionNodeHash(ListNode headA, ListNode headB) {
    HashSet<ListNode> nodesInB = new HashSet<ListNode>();
    // 将链表B的所有节点存入哈希表
    while (headB != null) {
        nodesInB.add(headB);
        headB = headB.next;
    }
    // 遍历链表A,检查是否有节点在哈希表中
    while (headA != null) {
        if (nodesInB.contains(headA)) {
            return headA; // 找到交点
        }
        headA = headA.next;
    }
    return null; // 没有交点
}

四、 测试

代码实现:

ini 复制代码
//测试
    public static void main(String[] args) {
        linkedHasNode solution = new linkedHasNode();

        // 创建两个链表
        ListNode common = solution.new ListNode(8);
        common.next = solution.new ListNode(4);
        common.next.next = solution.new ListNode(5);

        ListNode headA = solution.new ListNode(4);
        headA.next = solution.new ListNode(1);
        headA.next.next = common; // 链表A连接到公共节点

        ListNode headB = solution.new ListNode(5);
        headB.next = solution.new ListNode(0);
        headB.next.next = solution.new ListNode(1);
        headB.next.next.next = common; // 链表B连接到公共节点

        // 查找交点
        //双指针法
//        ListNode intersection = solution.getIntersectionNode(headA, headB);
        //哈希法
//        ListNode intersection = solution.getIntersectionNodeHash(headA, headB);
        //差值法
        ListNode intersection = solution.getIntersectionNodeLength(headA, headB);
        if (intersection != null) {
            System.out.println("Intersection at node with value: " + intersection.val);
        } else {
            System.out.println("No intersection.");
        }
    }

测试结果:

sql 复制代码
Intersection at node with value: 8

五、方法对比与选择

方法 时间复杂度 空间复杂度 核心优势 适用场景
双指针法 O(m+n) O(1) 最优空间,无额外存储 内存敏感场景
长度差法 O(m+n) O(1) 逻辑直观,易理解 新手学习、代码可读性优先
哈希集合法 O(m+n) O(n) 实现简单,无需处理长度差 空间充足、追求代码简洁

实际工程中优先选择双指针法,兼顾时间与空间效率。

相关推荐
颜进强1 分钟前
21 · NestJs AsyncProviders 异步提供者:useFactory 返回 Promise 之后,容器发生了什么
前端·后端·ai编程
Mikko75 分钟前
JVM 线上排查实战(七):jps 看不到进程、jstack 报不允许的操作怎么办?attach 失败的六种情况实测
java·运维·jvm·后端
小宋102118 分钟前
Agent 工具升级如何不破坏线上:Tool Schema 版本兼容与契约测试
java·人工智能·后端·spring
geovindu24 分钟前
rust: Flyweight Pattern
开发语言·后端·设计模式·rust·享元模式·结构型模式
我的xiaodoujiao25 分钟前
Django 基础知识详细图文教程 13-Django 模型定义与使用 3
数据库·后端·python·测试工具·oracle·django
SimonKing27 分钟前
QClaw关停之后:一个工具的退场,一段关系的告别
java·后端·程序员
不一样的少年_1 小时前
别用前端思维写后端:一张 5MB 图片,为什么能撑爆内存?
前端·后端·图片资源
夜之眷属1 小时前
记一次战斗服务器 CPU 打满 100% 且“无法恢复“的排查
java·linux·运维·服务器·后端·性能优化
郝学胜-神的一滴1 小时前
C++ Templates 07:编译报错和调试手段与实战技巧
开发语言·c++·后端·软件工程·visual studio
谢亮_vipxieliang2 小时前
PHP 8 新特性:构造器属性提升与实战
开发语言·后端·php