剑指 Offer 52. 两个链表的第一个公共节点(160. 相交链表&剑指 Offer II 023. 两个链表的第一个重合节点)
题目:
思路:
【1】借用辅助空间的方式:利用set存储其中的一条链表,当另一条链表遍历的时候在set判断是否已存入。
【2】利用倒叙的思维,由于相交的部分都是末尾,所以可以考虑剪刀长链表的较长部分,因为这部分绝对不可能相交,如A链表是5,B链表是8,那么相交的部分是小于等于5的。
【3】基于步骤2中的思维在简化一下,既然相交在末尾,那么我把这两条链表组合起来如:A=A+B,B=B+A。那么此时长度必然相等,而且相对于步骤2中的时间复杂度为O(2N),这里时间复杂度为O(N+M)【这里N,M为链表长度,且N>M】,虽然时间复杂度都为O(N),但毕竟还是有些许差距。
代码展示:
//链表相加 //时间1 ms击败98.27% //内存44.3 MB击败54.76% //时间复杂度:O(m+n),其中 m 和 n 是分别是链表 headA 和 headB 的长度。两个指针同时遍历两个链表,每个指针遍历两个链表各一次。 //空间复杂度:O(1)。 class Solution { ListNode getIntersectionNode(ListNode headA, ListNode headB) { if (headA == null || headB == null) { return null; } ListNode pA = headA, pB = headB; while (pA != pB) { pA = pA == null ? headB : pA.next; pB = pB == null ? headA : pB.next; } return pA; } } //长链表去头 //时间1 ms击败98.27% //内存44.6 MB击败29.35% //时间复杂度O(2N),可看做O(N),这里可以看做N>M //空间复杂度O(1) class Solution { ListNode getIntersectionNode(ListNode headA, ListNode headB) { int countA = 0,countB = 0; ListNode tem = headB,temp = headA; //优先计算出两个链表的长度,这里次数取最长的链表长度N while (temp != null || tem != null){ if (temp != null){ countA++; temp = temp.next; } if (tem != null){ countB++; tem = tem.next; } } int dif = Math.abs(countA-countB); tem = headB; temp = headA; //让最长的链表先走相差的步数 while (dif-- > 0){ if (countA > countB){ temp = temp.next; }else { tem = tem.next; } } //然后按相同的步数走,比对是否有重合之处 while (temp != null){ if (temp == tem) return temp; temp = temp.next; tem = tem.next; } return null; } } //使用辅助空间 //时间9 ms击败5.74% //内存43.8 MB击败96.21% //时间复杂度O(N+M),可看做O(N) //空间复杂度O(N) /** * 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) { HashSet<ListNode> set = new HashSet<>(); ListNode temp = headA; while (temp != null){ set.add(temp); temp = temp.next; } temp = headB; while (temp != null){ if (set.contains(temp)) return temp; temp = temp.next; } return null; } }