1、问题描述
输入两个链表,求这两个链表的第一个公共结点。
2、解题思路
-
边界条件:两个链表一个为空或者两个都为空,返回空
-
思路1:最简单的方法是使用两个指针对这两个链表进行遍历,在第一个指针遍历第一个链表中某个结点的时候,第二个指针遍历整个第二个链表,如果第二个指针遍历的某个结点与第一个指针遍历的结点相等,则该相等的结点为两个链表的第一个公共节点,这种方法的时间复杂度为O(mn)O(mn)O(mn)。
-
分析:如果两个单向链表有公共的结点,那么说明这两个链表从某个结点开始,它们的next指针都指向了同一个结点,但由于是单向链表的结点,每个结点只能有一个后继结点,所以从第一个公共结点之后,两个链表的所有结点都是相同的,如下图所示。
-
结论:从图中可以看出,如果两个链表有公共结点,那么第一个公共结点一定出现在两个链表的末尾。
-
思路2:根据结论,我们可以从两个链表的最后一个结点开始,依次向前比较,直至找到最后一个相等的结点为止。但是,我们知道,单向链表一般只能从前向后遍历,从后往前遍历怎么实现呢?我们可以通过两个辅助栈实现这一过程,一开始,把这两个链表分别压入两个栈中,然后比较这两个栈的栈顶元素,如果栈顶元素相同,则将两个栈的栈顶元素同时弹出,如果不相同,那么第一个公共结点为上次弹出的元素。这种方法的时间复杂度为O(m+n)O(m+n)O(m+n)(主要是对链表的压栈操作),空间复杂度为O(m+n)O(m+n)O(m+n)(两个栈的大小)。
-
思路3:其实,我们也可以不借助辅助栈来找到第一个公共结点,该方法的步骤是,先对两个链表分别遍历,获得这两个链表的长度,接着计算这两个链表的长度差k,然后在长链表上先走k步,紧接着在长短链表上同时遍历,找到的第一个相同的结点就是它们的第一个公共结点。这种方法省去了栈的空间开销,时间复杂度为O(m+n)O(m+n)O(m+n)
3、代码实现
/*
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 common_node = null;
ListNode p1 = pHead1;
ListNode p2 = pHead2;
int length1 = 0;
int length2 = 0;
while (p1 != null){
length1 += 1;
p1 = p1.next;
}
while (p2 != null){
length2 += 1;
p2 = p2.next;
}
int dis = 0;
ListNode long_list = null;
ListNode short_list = null;
if(length1 < length2){
dis = length2 - length1;
short_list = pHead1;
long_list = pHead2;
}
else {
dis = length1 - length2;
short_list = pHead2;
long_list = pHead1;
}
int i = 0;
while (i < dis){
long_list = long_list.next;
i += 1;
}
while(long_list != null){
if(long_list.val == short_list.val){
common_node = long_list;
break;
}
else {
long_list = long_list.next;
short_list = short_list.next;
}
}
return common_node;
}
}