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

力扣热题100之第160题:

方法一、差值步法:

这个方法的思路为先定义两个指针,分别指向两条链表的头节点,再一人一步往后走,如果这两条链表有相交,则两个指针必定会相遇,若没有相遇则返回null,说明两条链表没有相交。

但是上述情况是在两条链表长度相等的情况下,若这两条链表不相等怎么办呢?

我们可以先找到两条链表中较长的那条,让指向长链表的那个头指针先走它们两个链表的长度差值步数,比如链表A有八个节点,长度为8,链表B有七个节点,长度为7,则它们差值步数为1,即可以先让指向链表A的头指针先走1步,然后两指针再一起往前走,若相遇则说明两条链表相交。

具体代码如下:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        int lenA = 0; //定义链表A的长度lenA
        int lenB = 0; //定义链表B的长度lenB
        ListNode cur = headA; //新建一个指针cur指向链表A的头节点
        ListNode ret = headB; //新建一个指针ret指向链表B的头节点
        int dvalue = 0; //定义长度的差值dvalue
        //求出链表A的长度
        while(cur != null){
            lenA++;
            cur = cur.next;
        }
        //求出链表B的长度
        while(ret != null){
            lenB++;
            ret = ret.next;
        }
        //再将两指针重新指向头节点
        cur = headA;
        ret = headB;

        if(lenA > lenB){ //若链表A长,则cur指针先走dvalue步
            dvalue = lenA - lenB;
            while(dvalue != 0){
                cur = cur.next;
                dvalue--;
            }
        }else{  //若链表B长,则ret指针先走dvalue步
            dvalue = lenB - lenA;
            while(dvalue != 0){
                ret = ret.next;
                dvalue--;
            }
        }
        //最后两指针同时往前走
        while(cur != null && ret != null){
            if(cur == ret){ //若此时cur和ret相遇了,则说明两条链表有相交情况,返回开始相交的节点
                return cur;
            }
            cur = cur.next;
            ret = ret.next;
        }
        //当while循环遍历完时还没相遇,则不存在相交
        return null;
    }
}

方法二、哈希集合法(HashSet):

有题目可知,若两条链表相交了,则必然在这两条链表中会有至少一个完全相同的节点,于是我们就可以先遍历一条链表A,将其所有的节点记录下来,然后遍历另一条链表B,若链表B中存在一个及以上和链表A中一样的节点,那么就说明这两条链表相交了,反之则未相交。

这里我们可以使用哈希集合来存储节点,遍历链表B的节点时,依次判断该节点是否在哈希集合中,具体代码如下:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //定义一个哈希集合set
        Set<ListNode> set = new HashSet<>();
        //遍历链表A,将其节点全部存入哈希集合中
        while(headA != null){
            set.add(headA);
            headA = headA.next;
        }
        //遍历链表B各个节点,若该节点在集合中存在则说明相交
        while(headB != null){
            if(set.contains(headB)){
                return headB;
            }
            headB = headB.next;
        }
        //循环遍历完说明并没有节点在集合中存在,即不相交
        return null;
    }
}

方法三、双指针法

若两条链表相交,则图形类似于:

 因为两条链表相交,所以会有一段公共长度,如图c部分,我们创建两个指针nodeA和nodeB分别指向链表A和链表B。

让他们分别循环往前各走一步,当nodeA走完为空时,就让他前往链表B的头节点;nodeB也一样,当它走完时,让它前往A链表的头节点。之后同样继续循环往前走,因为两条链表是相交的,则nodeA和nodeB必然会相遇。

因为两条链表相交时,分为三部分:c部分是固定的,那么不管a部分和b部分谁长,走第二遍时,nodeA走过的路程为a部分长度加上b部分长度;nodeB走过的路程也为a部分长度加上b部分长度。

由此我们可以得出具体代码:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //当链表A和链表B有一条为null时,都不会相交
        if (headA == null || headB == null) {
            return null;
        }

        ListNode nodeA = headA;
        ListNode nodeB = headB;

        while (nodeA != nodeB) {
            if(nodeA == null){
                nodeA = headB;
            }else{
                nodeA = nodeA.next;
            }

            if(nodeB == null){
                nodeB = headA;
            }else{
                nodeB = nodeB.next;
            }
        }
        return nodeA;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_56960711/article/details/123284297