本篇博客会讲解力扣“160. 相交链表”的解题思路,这是题目链接。
老规矩,先来审题。这道题的题干有点长,简而言之,就是判断2个链表是否相交,如果相交就返回第一个相交结点,不相交就返回NULL。看看题目原文:
以下是几个示例:
以下是提示和进阶要求:
接下来讲解我想到的解题思路,如果有更好的思路,可以在评论区提出。
思路1
这道题本质上是2小问:
- 链表是否相交?
- 如果相交,请找出第一个相交结点。
对于第一个问题,判断方法其实很简单,如果尾结点相同,那么2个链表相交。
如果相交的话,如何找出第一个相交结点呢?我们可以在第一步找出尾结点的时候顺便统计2个链表的长度,并求出长度的差。接着让长链表先走差距步,2个链表再同时走,第一个相遇的结点就是我们要找的结点。
开始写代码:首先先找尾结点。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *tailA = headA, *tailB = headB;
// 找尾结点
while (tailA->next)
{
tailA = tailA->next;
}
while (tailB->next)
{
tailB = tailB->next;
}
}
如果尾结点不相同,则链表一定不相交。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *tailA = headA, *tailB = headB;
// 找尾结点
while (tailA->next)
{
tailA = tailA->next;
}
while (tailB->next)
{
tailB = tailB->next;
}
// 尾结点不同,链表不相交
if (tailA != tailB)
{
return NULL;
}
}
如果相交,就应该求出链表的第一个相交结点。我们得让长的链表先走差距步,所以应该先求出2个链表的长度,在前面找尾结点的同时就可以顺便求一下。注意为了严谨起见,lenA和lenB都被初始化成1,不然会少算一个结点。计算差距可以调用abs函数求绝对值。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *tailA = headA, *tailB = headB;
int lenA = 1, lenB = 1;
// 找尾结点,顺便求长度
while (tailA->next)
{
++lenA;
tailA = tailA->next;
}
while (tailB->next)
{
++lenB;
tailB = tailB->next;
}
// 尾结点不同,链表不相交
if (tailA != tailB)
{
return NULL;
}
// 长度差距,取差的绝对值
int gap = abs(lenA - lenB);
}
接下来我们得知道这2个链表谁长谁短。方法很简单,先假设A长B短,如果不是这样,就修正一下。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *tailA = headA, *tailB = headB;
int lenA = 1, lenB = 1;
// 找尾结点,顺便求长度
while (tailA->next)
{
++lenA;
tailA = tailA->next;
}
while (tailB->next)
{
++lenB;
tailB = tailB->next;
}
// 尾结点不同,链表不相交
if (tailA != tailB)
{
return NULL;
}
// 长度差距,取差的绝对值
int gap = abs(lenA - lenB);
// 判断链表谁长谁短
// 假设A长B短
struct ListNode *longList = headA, *shortList = headB;
// 不满足就修正
if (lenA < lenB)
{
longList = headB;
shortList = headA;
}
}
接下来让长链表先走差距步:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *tailA = headA, *tailB = headB;
int lenA = 1, lenB = 1;
// 找尾结点,顺便求长度
while (tailA->next)
{
++lenA;
tailA = tailA->next;
}
while (tailB->next)
{
++lenB;
tailB = tailB->next;
}
// 尾结点不同,链表不相交
if (tailA != tailB)
{
return NULL;
}
// 长度差距,取差的绝对值
int gap = abs(lenA - lenB);
// 判断链表谁长谁短
// 假设A长B短
struct ListNode *longList = headA, *shortList = headB;
// 不满足就修正
if (lenA < lenB)
{
longList = headB;
shortList = headA;
}
// 长链表先走差距步
while (gap--)
{
longList = longList->next;
}
}
2个链表同时走,直到相遇。相遇的结点就是第一个相交结点。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *tailA = headA, *tailB = headB;
int lenA = 1, lenB = 1;
// 找尾结点,顺便求长度
while (tailA->next)
{
++lenA;
tailA = tailA->next;
}
while (tailB->next)
{
++lenB;
tailB = tailB->next;
}
// 尾结点不同,链表不相交
if (tailA != tailB)
{
return NULL;
}
// 长度差距,取差的绝对值
int gap = abs(lenA - lenB);
// 判断链表谁长谁短
// 假设A长B短
struct ListNode *longList = headA, *shortList = headB;
// 不满足就修正
if (lenA < lenB)
{
longList = headB;
shortList = headA;
}
// 长链表先走差距步
while (gap--)
{
longList = longList->next;
}
// 同时走,直到相遇
while (longList != shortList)
{
longList = longList->next;
shortList = shortList->next;
}
// 相遇点即为第一个交点
return longList;
}
这样就通过了。
思路2
不过本题还有一个更简单的解法:2个链表同时向后走,如果走到NULL,就转移到另一个链表的头,直到相遇为止。若相遇点为NULL,则不相交,否则相遇点为第一个交点。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *curA = headA, *curB = headB;
while (curA != curB)
{
// 如果没走到空,就向后走
// 如果走到空,就转移到另一个链表的头
if (curA)
curA = curA->next;
else
curA = headB;
if (curB)
curB = curB->next;
else
curB = headA;
}
// 相遇点为NULL,则不相交
// 若相交,则相遇点为第一个交点
return curA;
}
这种解法是不是很巧妙?但是你大概率是想不到的,所以还是老老实实的用第一种解法吧。
本解法的原理是:设前缀为a和b,后缀为m(若不相交则m==0),若前缀相等,则走a(b)步相遇;若前缀不等,则走a+b+m步后相遇。
总结
相交链表问题的解题思路如下:
- 先找出尾结点,如果尾结点相同则相交,否则不相交。
- 找尾结点时顺便求出链表长度,计算出长度差距。
- 判断哪个链表长,哪个链表短的方法:先假设,如果不满足假设就修正。
- 让长链表先走差距步,再同时走,直到相遇,相遇点就是第一个交点。
- 巧妙的解法:大家同时向前走,如果没碰着,就换条道。
感谢大家的阅读!