[LeetCode] Intersection of Two Linked Lists 题解

前言

Intersection of Two Linked Lists也是LeetCode的Linked List Tag下的一道题,网上流传着多种做法。

题目

https://leetcode.com/problems/intersection-of-two-linked-lists/
Write a program to find the node at which the intersection of two singly linked lists begins.

QQ截图20160319101728.jpg

Notes:
If the two linked lists have no intersection at all, return null.
The linked lists must retain their original structure after the function returns.
You may assume there are no cycles anywhere in the entire linked structure.
Your code should preferably run in O(n) time and use only O(1) memory.

题意就是有两串链表,它们可能是有公共部分的,请求出两个链表的交叉点,返回之。
注意如果没有intersection,就返回NULL。解法不能改变原有链表的结构。另外保证链表中任何地方不存在环。最好实现O(n)的时间复杂度和O(1)的空间复杂度。

分析

这个题解法还是不少的。首先能够想到的就是暴力枚举,对于链表A中的每个节点ai,遍历整个链表B,检查B中是否有节点与ai重合。这显然是O(mn)的 时间。

第二种方案,弄个哈希表,遍历链表A,把每个节点的地址存储在哈希表中。然后遍历B,检查链表B中的每个节点bi——如果在哈希表中找到了,则bi就是交点。

第三种方案,长度计算法。设置指针p1、p2分别指向链表A和B的head。通过遍历记录两个链表的长度,lenA和lenB。要注意两个链表的长度可能不相同,如题目所给的样例,lenA=5,lenB=6,我们得到:lenB-lenA=1。根据这个结论,将指针p2从链表B的首节点开始移动1步,即指向第二个节点,p1指向链表A的首节点,然后同时开始遍历,每次走一步,当它们相等时,自然就是两个表的交点。

但是长度计算的方案仍然不是最简洁的做法,接下来我们讨论一个在LeetCode的Discuss区被称为“Really eyes opening algorithm!”的解法,它实际上是一种双指针法
首先做一个假设,如果题目的测试数据中,每一组链表的长度都相等,会怎么样?也就是说,对于上面的长度计算法,所有的lenB都等于lenA,会如何?显然,我们就不需要费神把指针挪来挪去——只需要扔两个指针到A和B的head,然后同时遍历,它俩第一次相等的时候自然就是intersection point。
然而事情没有这么完美,两个链表长度不同的情况,要是还同时遍历的话,它们之间总会出现一定的距离差距。

QQ截图20160319103638.jpg

但是我们可以发现,无论两个链表各自多长,是长度相等还是长度相差很大,它们都满足这样一个公式:a + c+ b = b + c +a

QQ截图20160319104117.jpg

注意这个公式中字母出现的顺序,这意味着我们可以这么做:维护两个指针pA和pB,初始分别指向A和B的head。然后让它们分别遍历整个链表,每步一个节点。当pA到达链表末尾时,让它指向B的头节点;而当pB到达链表末尾时,让它重新指向A的头节点。 继续遍历过程,如果pA在某一点与pB相遇(相等),则pA/pB就是交点。这个过程结束后,每个指针的“路程”都是a + b + c的长度。我们显然得出这样的论断,当两个指针都被重置过一次后,如果遍历继续到某个时刻它们相等,那么这个点一定就是intersection。
这个方案是O(n+m) Time Complexity, O(1) Space Complexity的,也是目前公认的最优解法。

代码

下面给出长度计算法和双指针法的代码:
长度计算法:

 ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *pa=headA,*pb=headB;
        int lengthA=0,lengthB=0;
        while(pa) {pa=pa->next;lengthA++;}
        while(pb) {pb=pb->next;lengthB++;}
        if(lengthA<=lengthB){
            int n=lengthB-lengthA;
            pa=headA;pb=headB;
            while(n) {pb=pb->next;n--;}
        }else{
            int n=lengthA-lengthB;
            pa=headA;pb=headB;
            while(n) {pa=pa->next;n--;}
        }
        while(pa!=pb){
            pa=pa->next;
            pb=pb->next;
        }
        return pa;
    }

双指针法:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
      ListNode * p1 = headA, * p2 = headB;
      if (p1 == NULL || p2 == NULL) return NULL;
      while (p1 != NULL && p2 != NULL && p1 != p2) {
        p1 = p1->next;
        p2 = p2->next;
        if (p1 == p2) return p1;
        if (p1 == NULL) p1 = headB;
        if (p2 == NULL) p2 = headA;
      }
    return p1;
    } 
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值