题目描述
输入两个无环的单链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
示例1
输入:{1,2,3},{4,5},{6,7}
返回值:{6,7}
说明:第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分,这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的。
题目分析
题目中所讲的链表的公共结点,是指相同的结点对象,而非只是简单的值相同,若两链表有公共结点,则有如下图的链表结构:
链表1和链表2的公共结点部分为{6,7},第一个公共结点为6,可以通过遍历链表1和链表2,判断结点对象第一个相同的即为第一个公共结点。
解题思路
方法1:使用hashset来保存结点
1.首先遍历一遍链表1,并将所有结点都放入到hashset中
2.在遍历链表2的过程,判断hashset中是否存在此结点
3.若是存在,则第一次发现存在的,就是第一个公共结点,直接返回;若是遍历完链表2,仍不存在,则返回null
方法2:使用双指针
使用两个指针变量同时遍历两个链表,因为非公共部分的长度不一定相同,所以遍历到相同部分的时间也不一样;
通过将链表1到达链表尾部后,继续指向链表2,而链表2则指向链表1,继续遍历,则这次遍历中两个指针cur1和cur2必定会相遇:
假设链表1非公共部分长度为A,链表2非公共部分长度为B,公共长度为C,则有:
(A+C) + B = (B+C) + A
即cur1和cur2第一次相遇的条件,当链表有公共结点时,cur1和cur2指向的是第一个公共结点;当没有公共结点时,cur和cur2都是null。
代码实现
方法1:使用hashset来保存结点
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { ListNode current1 = pHead1; ListNode current2 = pHead2; // 借助hashset HashSet<ListNode> set = new HashSet<ListNode>(); while (current1 != null) { // 遍历一遍链表1,将链表1中的结点都加入到set中 set.add(current1); current1 = current1.next; } while (current2 != null) { // 在遍历链表2的时候,若set中包含该结点,则此结点为第一个公共结点 if (set.contains(current2)) return current2; current2 = current2.next; } // 若都遍历完,则没有公共结点 return null; }
时间复杂度:O(n+m),n和m分别为链表1和链表2的长度,最差情况下没有公共结点,则需要遍历两个链表;
空间复杂度:O(n),需要在set中存储链表1的所有结点。
方法2:使用双指针
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { ListNode current1 = pHead1; ListNode current2 = pHead2; while(current1 != current2){ // 当链表1已经到末尾时,转到链表2遍历 current1 = current1 == null ? pHead2 : current1.next; // 当链表2已经到末尾时,转到链表1遍历 current2 = current2 == null ? pHead1 : current2.next; } // 退出循环的情况可能有两种:c1和c2均为第一个公共结点;c1和c2均为null return current1; }
时间复杂度:O(n+m),n和m分别为链表1和链表2的长度,最差情况下没有公共结点,则需要遍历两个链表;
空间复杂度:O(1),只需要两个变量来遍历链表1和链表2,空间复杂度为O(1)。