AC 此题,链表无敌!!!

刷过链表题目的小伙伴都应该有这样的体会,链表题最容易出错的就是 空指针异常 。做着做着“链断了”。因此,对于链表的题目来说,Coding 能力非常重要,通过大量题目的训练,练习对于 边界条件 判断的处理能力。

下面我们练习一道 非常综合 的题目,一题顶三题

题目

给定两个可能 有环 可能 无环 的单链表,头节点分别为 head1 和 head2 。

实现如下功能:如果两个链表相交,返回相交的第一个节点;如果不相交,返回 null 。

要求 :如果两个链表长度之和为 N,时间复杂度 O(N),空间复杂度 O(1)。

    // 单链表结构体定义
    public static class Node {
   
        public int value;
        public Node next;

        public Node(int value) {
   
            this.value = value;
        }
    }

要想完美解答该题,我们先来做另外几道题。

LeetCode:142.环形链表

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。不允许修改链表。

直接给出 力扣官方题解 的算法思路,使用 快慢指针 解题:

两个指针 fast 与 slow, 起始指向链表的头部。slow 每次向后移动一个位置, fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与 slow 指针在环中相遇。

设链表中环外部分的长度为 a。slow 指针进入环后,又走了 b 的距离与 fast 相遇。此时,fast 指针已经走完了环的 n 圈,因此它走过的总距离为:
a + n ( b + c ) + b = a + ( n + 1 ) b + n c a+n(b+c)+b=a+(n+1)b+nc a+n(b+c)+b=a+(n+1)b+nc
根据题意,任意时刻,fast 指针走过的距离都为 slow 指针的 2 倍。因此,我们有
a + ( n + 1 ) b + n c = 2 ( a + b ) ⟹ a = c + ( n − 1 ) ( b + c )   a+(n+1)b+nc=2(a+b) ⟹ a=c+(n−1)(b+c)  a+(n+1)b+nc=2(a+b)a=c+(n1)(b+c)
的等量关系,我们会发现:从相遇点到入环点的距离加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离。

因此,当 slow 与 fast 相遇时,fast 重新指向链表头部;fast 和 slow 每次向后移动一个位置。最终,它们会在入环点相遇。

    private static Node getLoopNode(Node head) {
   
        if (head == null || head.next == null || head.next.next == null) {
   
            return null;
        }
        Node slow = head.next;
        Node fast = head.next.next;
        while (slow != fast) {
   
            if (fast.next == null || fast.next.next == null) {
   
                return null;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        fast = head;
        while (slow != fast) {
   
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

LeetCode:160.相交链表

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

思路

如果两个链表相交且长度相同,那么两个链表都从头结点开始遍历,相遇时就找到了相交点;

如果两个链表长度不相同时,若能够也从到达结尾长度相同的点出发时,也一定能够给找到相交点。

因此,只需要将长链表走过一个长度差后,两链表再同时出发即可。

    private static Node noLoop(Node h1, Node h2) {
   
        if (h1 == null || h2 == null) {
   
            return null;
        }
        Node cur1 = h1;
        Node cur2 = h2;
        int n = 0;
        while (cur1.next != null) {
   
            cur1 = cur1.next;
            n++;
        }
        while (cur2.next != null) {
   
            cur2 = cur2.next;
            n--;
        }
        if (cur1 != cur2) {
   
            return null;
        }
        cur1 = n > 0 ? h1 : h2;
        cur2 = cur1 == h1 ? h2 : h1;
        n = Math.abs(n);
        while (n != 0) {
   
            n--;
            cur1 = cur1.next;
        }
        while (cur1 != cur2) {
   
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }

代码解释

n 来计算两个链表的长度差,cur1 = n > 0 ? h1 : h2;为了后续代码简单起见,将长链表重新用 cur1 指向,较短的链表用 cur2 指向。 cur1 先走过长度差 n 步后,两个链表指针同时出发,当 cur1 == cur2 时,指针指向同一片内存空间,即链表相交了。


上面两道题分别给出了 有环链表找入环点 问题和 无环链表找相交点 问题的解答。相信大家都学会了,接下来,我们来解决 文章开头提出的问题

题目

给定两个可能 有环 可能 无环 的单链表,头节点分别为 head1 和 head2 。实现如下功能:如果两个链表相交,返回相交的第一个节点;如果不相交,返回 null 。

假设两个链表分别是 A 和 B,则可以将该问题分为以下几种情况:

  1. A B 均无环。如果 A B两链表相交,则转化为了 无环链表找相交点 的问题。

  2. A 无环,B 有环;A 有环,B 无环;不可能相交

  3. A B 均有环:

  • 1)A 环 B 环不相交;

  • 2)A 环 B 环相交,有同一个入口点;

  • 3)A 环 B 环相交于不同的入口点。

有了上面两道题的解答,再来解决本问题就轻松很多,首先来看主函数:

    public static Node getIntersectNode(Node h1, Node h2) {
   
        if (h1 == null || h2 == null) {
   
            return null;
        }
        Node loop1 = getLoopNode(h1);
        Node loop2 = getLoopNode(h2);
        if (loop1 == null && loop2 == null) {
   
            return noLoop(h1, h2);
        }
        if (loop1 != null && loop2 != null) {
   
            return bothLoop(h1, loop1, h2, loop2);
        }
        return null;
    }

代码解释

如果两个链表一个为空,一定不会有环,直接返回空。
调用 getLoopNode 函数分别得到两个链表的入环结点。

loop1loop2 均为空,说明两个链表均为 无环单链表 。转化为了 情况 1,即 LeetCode:160.相交链表 问题。

loop1loop2 均不为空,说明两个链表均为 有环单链表 。转化为了 情况 3,此时就要判断是 情况 3 中的哪一种:

    private static Node bothLoop(Node h1, Node loop1, Node h2, Node loop2) {
   
        Node cur1 = null;
        Node cur2 = null;
        if (loop1 == loop2) {
   
            cur1 = h1;
            cur2 = h2;
            int n = 0;
            while (cur1 != loop1) {
   
                cur1 = cur1.next;
                n++;
            }
            while (cur2 != loop2) {
   
                cur2 = cur2.next;
                n--;
            }
            cur1 = n > 0 ? h1 : h2;
            cur2 = cur1 == h1 ? h2 : h1;
            n = Math.abs(n);
            while (n != 0) {
   
                n--;
                cur1 = cur1.next;
            }
            while (cur1 != cur2) {
   
                cur1 = cur1.next;
                cur2 = cur2.next;
            }
            return cur1;
        } else {
   
            cur1 = loop1.next;
            while (cur1 != loop1) {
   
                if (cur1 == loop2) {
   
                    return loop1;
                }
                cur1 = cur1.next;
            }
            return null;
        }
    }

代码解释

我们将 2)和 1)3)分成两大类进行判断:

if 条件进行的是 2)A 环 B 环相交,有同一个入口点 情况的判断。

入口点 作为两个链表的结束,红色范围 左侧 就转化为了 两个单链表找交点 的题目。

因此代码就很好理解了,先计算两个链表长度的差值,长链表先走过差值,之后两指针一起走,相遇即找到 入环点


else 条件进行的是 1)不相交 3)相交于不同的入口点 情况的判断。

1)如果两个环相交,那么 loop1 一定会在 一圈未走完 的情况下与 loop2 相遇。

2)若走了一圈又回到了 loop1 ,则说明两个环一定未相交。

至此,本题就得到了完美的解答。

总结

本题属于多道题目的杂糅,情况较多,需要理清思路一一破解。

通过对本题的分析,既熟练的掌握了 两个单链表的相交 问题,也掌握了 链表有无环 的判断方法。 一举多得!

一定要记得自己写一遍哦!注意边界条件的判断~

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-01-26 11:32:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-26 11:32:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-26 11:32:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-26 11:32:01       20 阅读

热门阅读

  1. 服务器宝塔安全需要修改的设置

    2024-01-26 11:32:01       37 阅读
  2. UnityUI看向相机

    2024-01-26 11:32:01       33 阅读
  3. mysql更新charset

    2024-01-26 11:32:01       29 阅读
  4. sealos apt&&yum安装 && sealos 部署k8s

    2024-01-26 11:32:01       37 阅读
  5. GET基于报错的sql注入利用-脱库

    2024-01-26 11:32:01       36 阅读
  6. 优雅的控制协程(goroutine)的并发数量

    2024-01-26 11:32:01       33 阅读
  7. Scikit-Learn 高级教程——自定义评估器

    2024-01-26 11:32:01       34 阅读
  8. JWT登录

    JWT登录

    2024-01-26 11:32:01      37 阅读