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

寻找两个链表交点的核心是"消除两链表长度差",常见方法分为空间复杂度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 小时前
Java RuoYi整合Magic-Api详解
java·后端·架构
自由的疯1 小时前
Java 实现TXT文件上传并解析的Spring Boot应用
后端·架构
开始学java2 小时前
抽象类和抽象方法
后端
华仔啊2 小时前
别再乱 new ArrayList!8 大 Java 容器选型案例,一篇看懂
java·后端
小码编匠2 小时前
手把手教会设计 WinForm 高DPI兼容程序,告别字体模糊与控件乱飞(.NET 4.6.1/.NET 6.0)
后端·c#·.net
秋难降3 小时前
MySQL 优化:告别 “996”,让系统高效运行
数据库·后端·mysql
追逐时光者3 小时前
很强!一款基于 .NET 构建、功能强大、通用的 2D 图形编辑器
后端·.net
mzlogin4 小时前
Java|FreeMarker 复用 layout
java·后端·freemarker
MrHuang9654 小时前
【创建线程的四种方式】
后端
双向334 小时前
Python 多线程日志错乱:logging.Handler 的并发问题
后端