剑指Offer-两个链表的第一个公共节点(延伸——判断链表有无环以及两链表相交问题)

35.两个链表的第一个公共节点(延伸——判断链表有无环以及两链表相交问题)

1.如何判断一个单链表是否有环?若有环返回第一个入环节点,无环返回空。

思路一:

从头遍历链表,用HashSet存储此链表的各个节点。如果此节点出现过,则返回它,若直到遍历到null都没有重复节点,就返回null,即不存在这样的结构。

public static Node findFirstIntersectNode(Node head) {
        if (head == null) return null;
        Node temp = head;
        HashSet<Node> hashSet = new HashSet<>();
        while (temp != null) {
            if (!hashSet.contains(temp)) {
                hashSet.add(temp);
            } else {
                return temp;
            }
            temp = temp.next;
        }
        return null;
    }

思路二:

快慢指针法。快慢指针都从头走,慢指针先走,快指针一次两步,慢指针一次一步,如果快指针走到了末尾,都没有与慢指针相遇,则返回null,如果相遇了,则必定有环,且在环上相遇的,此时快指针回到head,变成一次走一步;慢指针不动,然后两个指针再一起走,相遇的节点就是第一个入环节点。

 public static Node findFirstIntersectNode2(Node head) {
        if (head == null) return null;
        Node fast = head;
        Node slow = head;
        while (fast.next != null || fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                break;
            }
        }
        if (fast.next == null || fast.next.next == null)
            return null;
        else {
            fast = head;
        }
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }

        return fast;
    }

**=====================================================================================**

判断两条链表是否相交,若相交,则返回第一个相交的节点,若不相交则返回null.但注意,两条链表可能有环或无环.

思路:

我们可以通过上文的函数,得知每条链表是否有环,及其第一个入环节点。然后需要分情况讨论。

一、若两条链表都无环

在这里插入图片描述

若相交,就会是这种形式(注意不可能交叉,因为每个节点都只有一个后继节点)。实现方法有两种:

①HashSet。先遍历第一条链表,节点全部装入set,然后遍历第二条链表,看其各个节点是否在set中,第一个在set中的节点就是相交节点,若遍历完了都没有,就返回null。

public static Node firstIntersectNodeOfTwoNode(Node head1, Node head2) {
        if (head1 == null || head2 == null) return null;
        Node temp1 = head1;
        Node temp2 = head2;
        HashSet<Node> hashSet = new HashSet<>();
        while (temp1 != null) {
            hashSet.add(temp1);
            temp1 = temp1.next;
        }
        while (temp2 != null) {
            if (hashSet.contains(temp2)) {
                return temp2;
            }
            temp2 = temp2.next;
        }
        return null;
    }

②空间复杂度O(1)的方法。假设链表一和链表二不相交的长度是a,b,相交的长度为c。我们可以得到等式:a+c+b=b+c+a。也就是说,两个指针同时分别从两个链表一起走,那么总有一个时刻可以相遇,问题是怎么走。

方法是p从链表一开始走,走到null就返回去从链表二开始走,q从链表二开始走,走到null就返回去从链表一开始走。

class Solution {
    public ListNode findFirstCommonNode(ListNode head1, ListNode head2) {
        if(head1==null || head2==null) return null;
        
        ListNode p=head1;
        ListNode q=head2;
        while(p!=q)
        {
           if(p==null)
           {
               p=head2;
           }else
           {
               p=p.next;
           }
           if(q==null)
           {
               q=head1;
           }else
           {
               q=q.next;
           }
        }
        return p;
    }
}

二、若一个有环,一个无环,则必不可能相交。

因为每个节点都只有一个后继节点

三、两个带环链表

有如下三种情况:
在这里插入图片描述

如果两个带环链表第一个入环节点相同,就是情况二。此时我们研究的问题就无关于环了,第一个相交节点一定不在环上,我们就要研究“两条链表都无环的情况”。上文已给出。

如果不同,则遍历其中的一条链表,若没遇到另外一条的入环节点,则是情况一,无相交节点,返回null;若遇到了,则是情况三,此时返回任意一个入环节点即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值