题源: 面试题 02.07. 链表相交
方法:长度差法
解题思路是先遍历两个链表获取它们的长度,然后让长链表的指针先走两链表的长度差步,接着同时遍历两个链表,查找相交的节点。
解题步骤
- 初始化:创建两个指针
curA
和curB
,分别指向两个链表的头节点headA
和headB
。 - 计算长度:
- 遍历链表A,计算长度
lenA
。 - 遍历链表B,计算长度
lenB
。
- 遍历链表A,计算长度
- 长度调整:
- 如果链表B比链表A长,交换两个链表的头指针和长度,确保
curA
指向较长的链表。 - 计算两个链表的长度差
gap
。
- 如果链表B比链表A长,交换两个链表的头指针和长度,确保
- 移动长链表的指针:让
curA
先前移gap
步。 - 同步遍历:同时遍历两个链表,比较节点是否相同:
- 如果找到相同的节点,即为交点,返回该节点。
- 如果直到链表末尾都没有相同的节点,说明两个链表不相交,返回
NULL
。
代码分析
这个函数利用链表长度差来减少不必要的比较。通过确保两个链表从距离尾部相同距离的位置同步开始遍历,使得它们能在相交点相遇,如果有的话。
复杂度分析
- 时间复杂度:O(m+n),其中 m 和 n 是两个链表的长度。在最坏的情况下,我们可能需要遍历两个链表的全部长度。
- 空间复杂度:O(1),因为没有使用额外的空间,只有几个变量用于追踪长度和指针位置。
示例
例如,有两个链表A和B。链表A的节点是 1 -> 2 -> 3 -> 4 -> 5
,链表B的节点是 9 -> 4 -> 5
,其中值为4和5的节点是共享的。根据该算法,链表A和B的长度分别是5和3,长度差是2。因此,我们让链表A的指针先移动2步,然后两个指针同时移动,它们会在值为4的节点相遇,该节点就是所求的交点。
Code
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *curA = headA;
ListNode *curB = headB;
int lenA = 0, lenB = 0;
while(curA != NULL){ //求链表A的长度
curA = curA -> next;
lenA ++;
}
while(curB != NULL){ //求链表B的长度
curB = curB -> next;
lenB ++;
}
curA = headA; //让curAB恢复到原来的位置
curB = headB;
if(lenB > lenA){ //让链表A是最长的链表
swap(lenA, lenB);
swap(curA, curB);
}
int gap = lenA - lenB;
while(gap --) curA = curA -> next;
while(curA != NULL && curB != NULL){ //遍历AB,找到相交的节点
if(curA == curB) return curA;
curA = curA -> next;
curB = curB -> next;
}
return NULL;
}
};