题目描述
题目:输入两个链表,找出他们的第一个公共节点。链表的节点定义如下:
struct ListNode {
int val;
ListNode *next;
ListNode(int x)
: val(x), next(NULL)
{}
};
- 首先理解一下题意:两条链表有公共节点是什么情况?
- 公共节点即它们的next指向相同,值相同,所以若两条链表有公共节点的话,它们的形状会是一个 Y 型,而非 X 型,如下图所示:
解题思路
- 我们很容易想到的解题思路是:遍历两条链表,遍历第一条链表的时候,每遍历一个节点都要和第二条链表的每一个节点去比较。若有一个节点一样则说明两条链表在该节点处重合,于是就找到了它们的公共节点。此时分析这个算法的复杂度:若两条链表的长度分别为m,n,则该方法的时间复杂度为:O(mn)
- 如何降低我们的时间复杂度呢?我们可以换一种思路,如果两条链表有公共节点,则它们的尾部一定是重合的,若我们从后向前比较,则最后一个重合的节点即为我们的第一个重合节点。我们遍历链表只能从前向后遍历,但是我们比较却是从后向前比较,我们很容易将其与栈”后进先出”的特点相对应。将两条链表的节点依次入栈,每次比较栈顶元素,若相同,则弹出继续比较下一个,直到找到最后一个相同节点
- 我们分析上述算法的复杂度:我们借助了两个辅助栈,若两条链表的长度分别为m,n,则空间复杂度为O(m+n),时间复杂度也为O(m+n),和第一种方法比,时间复杂度降低,但是增加了空间的消耗
- 第三种思路:联想我们做过的关于链表的面试题中,譬如:找倒数第k个节点、判断链表是否带环等,解决这些问题我们采用的思路是双指针法,我们可以应用一下这个思路,假设两条链表的长度分别为m,n (m > n),那么我们可以让第一条链表先走(m-n)步,此时这两条链表到达尾节点的长度相同,此时指向两条链表的指针同时走,走一步,进行比较,直到找到第一个公共节点
- 第三种思路的前提是:我们必须要知道两条链表的长度,这不难,分别遍历即可。此时的时间复杂度为O(m+n),与第二种思路比,我们的空间效率得到了提升
- 整理好思路后我们就可以开始写代码了,此处我们采用第三种思路:
代码实现
int LengthOfList(ListNode* pHead)
{
if (pHead == NULL)
return 0;
int length = 0;
while (pHead != NULL)
{
++length;
pHead = pHead->next;
}
return length;
}
ListNode* WalkStep(ListNode* pHead, int step)
{
while (step--)
{
pHead = pHead->next;
}
return pHead;
}
ListNode* FindFirstCommonNode(ListNode *pHead1, ListNode *pHead2)
{
int l1 = LengthOfList(pHead1);
int l2 = LengthOfList(pHead2);
int step = l1 - l2;
if (l1 > l2)
pHead1 = WalkStep(pHead1, l1 - l2);
else
pHead2 = WalkStep(pHead2, l2 - l1);
while (pHead1 != NULL)
{
if (pHead1 == pHead2)
return pHead1;
pHead1 = pHead1->next;
pHead2 = pHead2->next;
}
return NULL;
}
void PrintList(ListNode* head)
{
while (head != NULL)
{
printf("%d ->", head->val);
head = head->next;
}
printf("over\n");
}
int main()
{
ListNode* node1 = new ListNode(1);
ListNode* node2 = new ListNode(2);
ListNode* node3 = new ListNode(3);
ListNode* node4 = new ListNode(4);
ListNode* node5 = new ListNode(5);
ListNode* node6 = new ListNode(6);
ListNode* node7 = new ListNode(7);
node1->next = node2;
node2->next = node3;
node3->next = node6;
node6->next = node7;
node4->next = node5;
node5->next = node6;
PrintList(node1);
PrintList(node4);
ListNode* node = FindFirstCommonNode(node1, node4);
cout << "第一个公共节点为:" << node->val << endl;
return 0;
}
- 程序运行结果为: