力扣-->#160. 相交链表-->链表-简单(哈希集合、双指针)

160. 相交链表

题目描述:
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:

 


 

题目数据保证整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。

自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被视作正确答案 。

示例 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
0 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]

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

方法一:哈希集合
HashSet哈希集合是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取查找性能。保证元素唯一性的方式依赖于:hashCodeequals方法
所以方法一利用哈希集合首先存储A链表中的所有结点,之后再与B链表进行判断,根据哈西元素的唯一性,当判断到同一元素时,之后的每个元素也都指向同一个,即当前节点就是两个链表相交的节点。

利用哈利集合的方法简单明了易懂,但对哈希集合的掌握并不熟练,需要注意哈希集合中的常用方法,并且与哈希映射区分开来:
哈希集合:
add(value):向哈希集合中插入一个值。
contains(value) :返回哈希集合中是否存在这个值。
remove(value):将给定值从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。
哈希映射:
put(key, value):向哈希映射中插入(键,值)的数值对。如果键对应的值已经存在,更新这个值。
get(key):返回给定的键所对应的值,如果映射中不包含这个键,返回-1。
remove(key):如果映射中存在这个键,删除这个数值对。

执行结果:通过
执行用时:7 ms, 在所有 Java 提交中击败了23.91%的用户
内存消耗:42.4 MB, 在所有 Java 提交中击败了5.01%的用户
通过测试用例:39 / 39

/**
 * 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 temp=headA;    
        Set<ListNode> visited=new HashSet<ListNode>();  
        while(temp!=null){
            visited.add(temp);  //将链表A中的结点逐个添加到哈希集合中
            temp=temp.next;
        }
        temp=headB;     //链表A添加完毕temp结点就没啥用了,可以直接赋值为B链表的头结点
        while(temp!=null){
            if(visited.contains(temp)){ //判断集合中是否包含了与B链表重合的A链表中的结点
                return temp;
            }
            temp=temp.next;
        }
        return null;
    }
}

方法二:双指针
双指针的方法更巧妙,而且实现了空间复杂度O(1),假设链表A的前相交前的长度为a,链表B相交前的长度为b,两段链表相交后的长度均为c,具体思路如下:
1.先判断A链表与B链表是否为空,若有任意为空,则说明不相交;
2.判断两链表是否相交:
        若两链表相交前长度相等,即a==b,则第一次遍历即可得到相交节点;
        若两链表相交前长度不等,即a!=b,则当指向链表A的临时指针tempA为空时,将tempA指向headB;反之,则当指向链表B的临时指针tempB为空时,将tempB指向headA。这么做的目的是,
        2.1若两链表相交,当tempA遍历a+c+b次,tempB遍历b+c+a次后,两个指针相遇,此时两指针均指向同一节点,直接返回即可;
        2.2当两链表不相交时,tempA遍历a+c+b次,tempB遍历b+c+a次后,两个指针并不会相遇,而是均指向null,也直接返回即可;
3.最后的返回结果即为此次判断的最终结果。
不得不说——妙啊!!!且空间复杂度为O(1)!

执行结果:通过
执行用时:1 ms, 在所有 Java 提交中击败了99.98%的用户
内存消耗:41.2 MB, 在所有 Java 提交中击败了63.40%的用户
通过测试用例:39 / 39

/**
 * 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) {//双指针
        if(headA==null || headB==null){
            return null;
        }
        ListNode tempA=headA;
        ListNode tempB=headB;
        //不相交-->两个指针分别遍历完两个链表后同时指向null
        //相交---->两个链表遍历到相同节点,
        while(tempA!=tempB){
            tempA= tempA==null?headB:tempA.next;
            tempB= tempB==null?headA:tempB.next;
        }
        return tempA;
    }
}
/**
两个三目表达式等价于:
    if(tempA==null){
            tempA=headB;
        }else{
            tempA=tempA.next;
        }
        if(tempB==null){
            tempB=headA;
        }else{
            tempB=tempB.next;
        }
 */

平平无奇小白程序媛一枚,欢迎各位大佬交流指教,如有不正确的地方,欢迎留言改正,谢谢!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值