### 这道题分两种情况讨论:链表是否存在环(询问面试官)。一些额外的说明:两个链表,第一个是链表A,长度为m,第二个是链表B,长度为n。
---
#### 无环
**第一种方法**:朴素的方法是将以链表A的每一个节点为key,对着链表B做一次遍历,直到找到第一个公共节点,或者没有公共节点,直接返回nullptr。时间复杂度为O(mn),空间复杂度为O(1)。这种方法的一种直接改进是使用哈希表保存链表A的每一个节点,因为之前的方法主要是查找消耗时间,这样做是以空间换时间(哈希表需要O(m)的空间保存),时间复杂度为O(m + n)。
**第二种方法**:不使用额外空间,但是时间也是O(m + n)。先遍历两个链表(因为没有环,所以直接遍历就可以),求出它们的长度 m 和 n。然后长的链表先走 | m - n | 步,之后再同时向前走,直到找到第一个公共节点。
下面实现第二种方法:
```
// 返回值:有公共节点,则返回第一个,否则,返回nullptr
SList* findFirstCommonNode(SList *list1, SList *list2)
{
unsigned int lengthOfList1 = getListLength(list1);
unsigned int lengthOfList2 = getListLength(list2);
SList *longList = list1, *shortList = list2;
unsigned int deltaOfLength = lengthOfList1 - lengthOfList2;
if (lengthOfList1 < lengthOfList2) {
longList = list2;
shortList = list1;
deltaOfLength = lengthOfList2 - lengthOfList1;
}
// 长链表先走 deltaOfLength 步
for (unsigned int i = 0; i < deltaOfLength; ++i) {
longList = longList->m_next;
}
while ( (longList != nullptr) &&
(shortList != nullptr) &&
(longList != shortList)) {
longList = longList->m_next;
shortList = shortList->m_next;
}
return longList;
}
// 求链表长度
unsigned int getListLength(SList *list)
{
unsigned int length = 0;
while (list != nullptr) {
++length;
list = list->m_next;
}
return length;
}
```
#### 不知道是否有环
这种情况我们需要先[判断链表是否有环](https://www.kancloud.cn/persuez/algorithm/858057)。
1. 两个链表都没有环, 按上面无环的算法走;
2. 一个有环,一个无环,这两个链表不可能相交,返回nullptr;
3. 两个都有环,此时两条链表如果相交,那么一定共享这个环。但如果相交的话,我们取哪个作为第一个公共节点呢?有人取下面的公共节点pos2作为两个链表的最后一个节点,然后用无环的方法求第一个公共节点,但我觉得似乎也没有什么特别意义。所以我们的主要任务就判断这个环是不是共有的就好了。我们在判断是否有环时返回两个链表的相遇点,记为pos1,pos2。然后我们在pos1点设置快慢指针,慢指针每次走一步,快指针每次走两步,那么在它们再次相遇时慢指针(或者快指针和快指针的m_next)一定遍历了整个环,而这个环是共享的,所以如果pos2也在这个环中,那么这两个链表就是相交的。(这里相当提供了一种方法遍历环)