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

寻找两个链表交点的核心是"消除两链表长度差",常见方法分为空间复杂度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) 实现简单,无需处理长度差 空间充足、追求代码简洁

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

相关推荐
catchadmin1 小时前
PHP 快速集成 ChatGPT 用 AI 让你的应用更聪明
人工智能·后端·chatgpt·php
西阳未落5 小时前
C++基础(21)——内存管理
开发语言·c++·面试
callJJ5 小时前
从 0 开始理解 Spring 的核心思想 —— IoC 和 DI(2)
java·开发语言·后端·spring·ioc·di
ANYOLY6 小时前
Redis 面试宝典
数据库·redis·面试
珍宝商店6 小时前
前端老旧项目全面性能优化指南与面试攻略
前端·面试·性能优化
你的人类朋友7 小时前
JWT的组成
后端
北风朝向8 小时前
Spring Boot参数校验8大坑与生产级避坑指南
java·spring boot·后端·spring
ShineSpark9 小时前
C++面试11——指针与引用
c++·面试
canonical_entropy9 小时前
一份关于“可逆计算”的认知解码:从技术细节到哲学思辨的完整指南
后端·低代码·deepseek
趙卋傑9 小时前
项目发布部署
linux·服务器·后端·web