Leetcode-160 相交指针

题目

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

图示两个链表在节点 c1 开始相交

 

 

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headAheadB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

 

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

 

 

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

 

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 1 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listAlistB 没有交点,intersectVal0
  • 如果 listAlistB 有交点,intersectVal == listA[skipA] == listB[skipB]

 

进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

错误思路

原本思路是进行双循环遍历,即遍历A中每个节点和B中的每个节点比较,如果发现相同的就返回,但是这样是错的,因为可能找到的是在第一个相交节点后面的节点

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode intersectionNode = null;
        if(headA != null && headB != null){
            ListNode currA = headA;
            ListNode currB = headB;
            boolean flag = false;
            while(currA != null){
                while(currB != null){
                    if(currA.val == currB.val){
                        flag = true;
                        break;
                    }
                    else{
                        currB = currB.next;
                    }
                }
                if(flag){
                    intersectionNode = currA;
                    break;
                }
                else{
                    currA = currA.next;
                    currB = headB;
                }
            }
        }
        return intersectionNode;
    }
}

双指针法

设置两个指针Pa和Pb,分别指向HeadA和HeadB,用于遍历A和B,Pa遍历完A后从B的初始节点遍历B,Pb遍历完B后从A的初始节点遍历A,直到两者遍历到具有相同值的节点或者是null节点。方法的原理是两者遍历到相同节点时遍历的节点数量相同。

 注意:Pa和Pb遍历到相同节点就是相交,无需比较Pa和Pb的val值

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //首先判断不可能相交的情况:headA和headB有一个为空链表
        if(headA == null || headB == null){
            return null;
        }
        else{
            ListNode Pa = headA;
            ListNode Pb = headB;
            //Pa和Pb走到相同的节点时就是相交节点,不需要判断Pa和Pb的val值是否相等
            while(Pa != Pb){
                Pa = Pa == null ? headB : Pa.next;
                Pb = Pb == null ? headA : Pb.next;
            }
            //循环结束,Pa和Pb走到相同节点,此时如果都是null节点则返回null节点,表示不相交
            return Pa;
        }
    }
}

哈希集合法

注意题目描述,相交的节点就是地址相同的节点,无需比较值。A中每个节点地址都是不同的,只需将A中每个节点放入哈希集合中(不是将值放入哈希集合中),按顺序遍历B中的节点,看集合中是否有B中的节点即可。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode tempNode = headA;
        Set<ListNode> tempSet = new HashSet<ListNode>();

        //注意题目描述,相交的节点就是地址相同的节点,无需比较val值!!
        while(tempNode != null){
            //加入的是节点而不是节点的值
            tempSet.add(tempNode);
            tempNode = tempNode.next;
        }

        tempNode = headB;
        while(tempNode != null){
            if(tempSet.contains(tempNode)){
                return tempNode;
            }
            else{
                tempNode = tempNode.next;
            }
        }
        //遍历结束没有节点在集合中,说明不相交,直接返回null
        return null;
    }
}

学到和回忆了

  • Java中的Set 集合
  • Java中,0和1不能自动转换成boolean类型,必须显示定义boolean类型。布尔类型 boolean 只有两个可能的值:true 和 false。

参考

链表技巧总结

  • 新建链表时,要有一个头节点,还要创建一个尾节点,用于尾插建立链表
  • 一般设置一个哨兵节点来建立新链表,这样可以处理原链表为空的情况
posted @ 2022-03-30 20:46  心空之上  阅读(25)  评论(0)    收藏  举报