输入两个无环的单向链表,找出它们的第一个公共结点,如果没有公共节点则返回空。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
数据范围: n≤1000
要求:空间复杂度 O(1),时间复杂度 O(n)
例如,输入{1,2,3},{4,5},{6,7}时,两个无环的单向链表的结构如下图所示:
输入描述:
输入分为是3段,第一段是第一个链表的非公共部分,第二段是第二个链表的非公共部分,第三段是第一个链表和第二个链表的公共部分。 后台会将这3个参数组装为两个链表,并将这两个链表对应的头节点传入到函数FindFirstCommonNode里面,用户得到的输入只有pHead1和pHead2。
返回值描述:
返回传入的pHead1和pHead2的第一个公共结点,后台会打印以该节点为头节点的链表。
示例1
输入:{1,2,3},{4,5},{6,7}
返回值:{6,7}
说明:
第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分
这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的
示例2
输入:{1},{2,3},{}
返回值:{}
说明:2个链表没有公共节点 ,返回null,后台打印{}
思路一 Set或List第一个链表,遍历第二个链表找相同节点
把第一个链表放入Set或者List,然后遍历第二个链表,返回第二个链表中第一个包含在Set或者List中的节点,没有返回null
代码:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
Set<ListNode> listNode1 = new HashSet<>(); //这里执行没有问题,但是在牛客会有红色波浪下划线,不太清除是什么原因,但是提交成功了
while (pHead1 != null) {
listNode1.add(pHead1);
pHead1 = pHead1.next;
}
if (!listNode1.isEmpty()) {
while(pHead2!=null){
if(listNode1.contains(pHead2)){
return pHead2;
}else{
pHead2 = pHead2.next;
}
}
}
return null;
}
}
思路二 双指针
两个指针,一个指针从第一个链表开始,走完之后走第二个链表;一个指针从第二个链表开始,走完之后走第一个链表,这两个指针走过的长度是相同的。如果有公共链表,两个链表长度相同的话,两个指针在遍历自己的链表时就会在第一个公共节点相遇;在两个链表长度不同的情况下,那两个指针在走另一个链表的时候,最后的公共部分必然是同步去走的,会在第一个公共节点相遇。
我描述的黏黏糊糊的,看看别人的描述吧:
使用两个指针 a,b 分别指向两个链表 pHead1,pHead2的头结点,然后同时分别逐结点遍历,当 a 到达链表 pHead1的末尾时,重新定位到链表 pHead2的头结点;当 b 到达链表 pHead2 的末尾时,重新定位到链表 pHead1的头结点。当双指针相遇时,所指向的结点就是第一个公共结点
1.指针a、b分别在pHead1、pHead2上同时行走
2.当指针a走到pHead1最后一个节点5时,指针b指向pHead2节点4
3.指针a指向pHead2头节点,此时指针b指向pHead2最后一个节点5
4.指针b指向pHead1头节点,双指针继续走
5.当双指针在pHead1、pHead2的节点3处相遇,节点3即为双链表第一个公共节点
上代码:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
ListNode head1 = pHead1;
ListNode head2 = pHead2;
int num1 = 0;
int num2 = 0;
while(num1<=1 && num2<=1){
if(head1!=head2){
head1 = head1.next;
head2 = head2.next;
if(head1==null){
head1 = pHead2;
num1+=1;
}
if(head2==null){
head2 = pHead1;
num2+=1;
}
}else{
return head1;
}
}
return null;
}
}
思路三 统计链表长度,长的先走,同样长度再一起走
这是看到其他人的思路,感觉思路跟上面双指针很类似,只是表现方式不同
先统计两个链表的长度,如果两个链表的长度不一样,就让链表长的先走,直到两个链表长度一样,这个时候两个链表再同时每次往后移一步,看节点是否一样,如果有相等的,说明这个相等的节点就是两链表的交点,否则如果走完了还没有找到相等的节点,说明他们没有交点,直接返回null即可
代码:
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
ListNode head1 = pHead1;
ListNode head2 = pHead2;
int num1 = getLength(pHead1);
int num2 = getLength(pHead2);
int num = num1 - num2;
if (num > 0) {
head1 = getListNode(head1, num);
} else if (num < 0) {
head2 = getListNode(head2, num * (-1));
}
while (head1 != head2) {
head1 = head1.next;
head2 = head2.next;
}
return head1;
}
private int getLength(ListNode head) {
int num = 0;
while (head != null) {
num += 1;
head = head.next;
}
return num;
}
private ListNode getListNode(ListNode head, int num) {
while (num > 0) {
head = head.next;
num -= 1;
}
return head;
}
}