题目
输入两个链表,找出它们的第一个公共结点。
分析
这个问题一种常想到的方法就是两层循环遍历,外层循环遍历链表A,对A中每个节点,遍历链表B,如果在B中找到,说明这个节点是第一个公共节点,但是这样的方法时间复杂为mn,一般是不允采用的。
所以我们需要分析更深层次的问题,找到其中的规律,我们如果动手画一下一般的链表图就能够发现两个链表的第一个公共节点之后的样子一定是如下表示(由此可以发现有时候动手是很重要的)
![]()
可以发现两个链表在第一个节点重合之后不会再分开了
简单多说一句不会分开的原因,因为单向链表的节点只有一个nextNode指向下一个节点,那么如果该节点重合了,那么后面的节点一定是同一个
如果直观点对这个图形进行解释就是两个链表重合之后呈现一个Y型而不是一个X型。方法一:
我们可以使用栈的特点来解决这个问题:分别把两个链表的结点放入两个栈里,这样两个链表的尾结点就位于两个栈的栈顶,接下来比较两个栈顶的结点是否相同。如果相同,则把栈顶弹出接着比较下一个栈顶,直到找到最后一个相同的结点。
方法二:
我们可以首先遍历两个链表得到它们的长度,就能知道哪个链表比较长,以及长的链表比短的链表多几个结点。在第二次遍历的时候,在较长的链表上先走若干步,接着再同时在两个链表上遍历,找到的第一个相同的结点就是它们的第一个公共结点。
代码
方法一:
public static Node FindFirstCommonNode(Node head1, Node head2)
{
if(head1 == null || head2 == null)
{
return null;
}
Stack<Node> stack1 = new Stack<Node>();
Stack<Node> stack2 = new Stack<Node>();
while(head1 != null)
{
stack1.Push(head1);
head1 = head1.nextNode;
}
while(head2 != null)
{
stack2.Push(head2);
head2 = head2.nextNode;
}
Node node1 = null;
Node node2 = null;
Node common = null;
while(stack1.Count > 0 && stack2.Count > 0)
{
node1 = stack1.Peek();
node2 = stack2.Peek();
if (node1.key == node2.key)
{
common = node1;
stack1.Pop();
stack2.Pop();
}
else
{
break;
}
}
return common;
}
方法二:
public static Node FindFirstCommonNode(Node head1, Node head2)
{
// 得到两个链表的长度
int length1 = GetListLength(head1);
int length2 = GetListLength(head2);
int diff = length1 - length2;
Node headLong = head1;
Node headShort = head2;
if (diff < 0)
{
headLong = head2;
headShort = head1;
diff = length2 - length1;
}
// 先在长链表上走几步
for (int i = 0; i < diff; i++)
{
headLong = headLong.nextNode;
}
// 再同时在两个链表上遍历
while (headLong != null && headShort != null && headLong != headShort)
{
headLong = headLong.nextNode;
headShort = headShort.nextNode;
}
Node commonNode = headLong;
return commonNode;
}
private static int GetListLength(Node head)
{
int length = 0;
Node tempNode = head;
while (tempNode != null)
{
tempNode = tempNode.nextNode;
length++;
}
return length;
}