题目:输入两个链表,找出它们的第一个公共结点。
测试用例:
1、功能测试:输入的两个链表有公共节点(第一个公共节点在链表头结点、中间、末尾);输入的两个链表没有公共节点。
2、负面测试:输入的链表头结点为空。
方法一:暴力法,时间复杂度为 O(mn)。
思路:在第一链表上顺序遍历每个结点,每遍历到一个结点的时候,在第二个链表上顺序遍历每个结点。如果在第二个链表上有一个结点和第一个链表上的结点一样,说明两个链表在这个结点上重合,于是就找到了他们的公共结点。如果第一个链表的长度为m,第二个链表的长度为n,显然该方法的时间复杂度为O(mn).
方法二:利用两个辅助栈,时间复杂度为 O(m + n),空间复杂度为 O(m + n)
思路:如果两个链表有公共结点,那么公共结点出现在两个链表的尾部。如果我们从两个链表的尾部开始往前比较,最后一个相同的结点就是我们要找的结点。可问题是在单向链表中,我们只能从头结点开始按顺序遍历,最后才能到达尾节点。最后到达的尾节点却要最先被比较,这听起来是不是像后进先出,也是我们就能想到用栈的特点来解决这个问题:分别把两个链表的结点放入两个栈里,这样两个链表的尾节点就位于两个栈的栈顶,接下来比较两个栈顶的结点是否相同。如果相同,则把栈顶弹出接着比较下一个栈顶,直到找到最后一个相同的结点。
方法三:最优解,时间复杂度为 O(m + n),不用额外的空间。
当两个链表的长度不相同时,如果我们从头开始遍历到达两个栈的尾节点的时间就不一致。其实解决这个问题还有一个更简单的办法:首先遍历两个链表得到他们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个结点。在第二次遍历的时候,在较长的链表上先走若干步,接着再同时在两个链表上遍历,找到第一个相同的结点就是他们的第一个公共结点。
public class test_fifty_two {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2){
int length1 = getLength(pHead1);
int length2 = getLength(pHead2);
if(length1 == 0 || length2 == 0)
return null;
ListNode longListNode = null;
ListNode shortListNode = null;
if(length1 > length2){
longListNode = pHead1;
shortListNode = pHead2;
}
else{
longListNode = pHead2;
shortListNode = pHead1;
}
//两条链表相差多少个节点
int dif = Math.abs(length2 - length1);
//长链表先走dif个节点
for(int i=0; i < dif; i++){
longListNode = longListNode.next;
}
//然后两个链表同时走
while(longListNode != null && shortListNode != null && longListNode != shortListNode){
shortListNode = shortListNode.next;
longListNode = longListNode.next;
}
return longListNode;
}
//获取链表的长度
private int getLength(ListNode pHead) {
if(pHead == null)return 0;
int length = 0;
while(pHead.next != null){
pHead = pHead.next;
length++;
}
return length;
}
}