Leetcode - 每日一题(160.相交链表)

原题链接

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

图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:

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

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at ‘2’
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,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 。

本人思路:
首先遍历两个链表,并依次将两个链表的节点存入队列当中。之后依次拿出两个队列中的尾部,若尾部相同,则表明有公共部分,则取出尾部,继续遍历队列,若不同,则表明不为公共部分,结束遍历。

代码如下:

class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ArrayDeque<ListNode> dequeA = new ArrayDeque<>();
        ArrayDeque<ListNode> dequeB = new ArrayDeque<>();
        //将链表A的节点依次放置到队列A当中
        while (headA!=null){
            dequeA.add(headA);
            headA=headA.next;
        }
        //将链表B的节点依次放置到队列B当中
        while (headB!=null){
            dequeB.add(headB);
            headB=headB.next;
        }
        //如果队列A或队列B有一个为空,则代表对应的链表也为空,直接返回null即可。
        if(dequeA.isEmpty()||dequeB.isEmpty()){
            return null;
        }
		
        ListNode answer=null;
        //当队列A不为空,且队列B不为空时
        while (!dequeA.isEmpty()&&!dequeB.isEmpty()){
        	//如果队列A尾部元素与队列B尾部元素相同,则表明有公共部分
            if(dequeA.getLast()==dequeB.getLast()){
            	//更新答案
                answer=dequeA.getLast();
                //移除队列A与队列B尾部元素
                //坑点:ArrayDeque的add()方法将元素添加到队列尾部(即index=elements.length-1 处)
                //	   而remove()方法却是移除队列头部元素(即index=0处)
                //		一度以为我代码写错了
                dequeA.removeLast();
                dequeB.removeLast();
            } else {
            	//如果队列A尾部元素与队列B尾部元素不同,则表明没有公共部分,跳出循环
                break;
            }
        }
        //返回答案
        return answer;
    }
}

本来觉得自己代码不错,一看结果

     执行用时:4 ms, 在所有 Java 提交中击败了 19.32% 的用户
     内存消耗:41.5 MB, 在所有 Java 提交中击败了 18.18% 的用户

顿时心肺骤停。

后来本人查看了官方给的题解有两个,无论哪个都比我的好(果然我是菜鸡)

官方题解思路一:
遍历第一个链表,并将其放入hashset中。之后遍历第二个链表,如果hashset中有第二个链表的节点,则表明该点即为相交点,否则继续遍历直到结束。

代码如下:

class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        HashSet<ListNode> set = new HashSet<>();
        //遍历链表A,并依次将元素放置入hashset中
        while (headA!=null){
            set.add(headA);
            headA = headA.next;
        }
        //遍历链表B,如果集合中包含该节点,则该节点即为相交点
        //否则继续向下遍历
        while (headB!=null){
            if(set.contains(headB)){
                return headB;
            }
            headB = headB.next;
        }
        //找不到相交点,直接返回null
        return null;
    }
}

测试结果:
执行用时:8 ms, 在所有 Java 提交中击败了 19.03% 的用户
内存消耗:42 MB, 在所有 Java 提交中击败了 10.15% 的用户

这个结果貌似还不如我的…




接下来讲述官方题解思路二(下面直接拿原文):

当链表 headA \textit{headA} headA headB \textit{headB} headB 都不为空时,创建两个指针 pA \textit{pA} pA pB \textit{pB} pB,初始时分别指向两个链表的头节点 headA \textit{headA} headA headB \textit{headB} headB,然后将两个指针依次遍历两个链表的每个节点。具体做法如下:

每步操作需要同时更新指针 pA \textit{pA} pA pB \textit{pB} pB

如果指针 pA \textit{pA} pA 不为空,则将指针 pA \textit{pA} pA 移到下一个节点;如果指针 pB \textit{pB} pB 不为空,则将指针 pB \textit{pB} pB 移到下一个节点。

如果指针 pA \textit{pA} pA为空,则将指针 pA \textit{pA} pA 移到链表 headB \textit{headB} headB 的头节点;如果指针 pB \textit{pB} pB 为空,则将指针 pB \textit{pB} pB 移到链表 headA \textit{headA} headA 的头节点。

当指针 pA \textit{pA} pA pB \textit{pB} pB 指向同一个节点或者都为空时,返回它们指向的节点或者 null \text{null} null

那么这段话说的啥意思呢 即在这里插入图片描述
假设链表A为线段A+线段C,链表B为线段B加线段C,则对于指针 pA \textit{pA} pA 从线段A头部出发,走到线段C尾部,再跳跃到线段B头部,走到线段C尾部,对于指针 pB \textit{pB} pB 从线段B头部出发,走到线段C尾部,再跳跃到线段A头部,走到线段C尾部。
那么对于指针 pA \textit{pA} pA 走过的路程即为 线段A+线段C+线段B+线段C,对于指针 pB \textit{pB} pB 走过的路程即为 线段B+线段C+线段A+线段C,我们可以发现指针 pA \textit{pA} pA 与指针 pB \textit{pB} pB 在前三部分走过的路程长度相同,若线段C长度不为0,即有相交部分,那么必定会到第四部分,也就是双方再次走到线段C开头时相遇。则我们可以得到相交点。

代码如下:

class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    	//如果链表A或链表B为空,直接返回null
        if(headA==null||headB==null){
            return null;
        }
        ListNode pA = headA;
        ListNode pB = headB;
        //当指针A与指针B指向节点不同时
        while (pA!=pB){
            if(pA!=null){
                pA = pA.next;
            }else {
                pA = headB;
            }
            if(pB!=null){
                pB = pB.next;
            }else {
                pB = headA;
            }
        }
        return pA;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值