剑指offer:37两个链表的第一个公共结点(注释)

本文介绍了三种解决寻找两个链表相交节点的方法:哈希集合法、双指针法和快慢指针法。通过哈希集合存储链表A的节点,然后遍历链表B检查是否存在公共节点;双指针法同步遍历两个链表,直至找到相同节点;快慢指针法先确定两个链表的长度差,然后让长链表先走差值距离,之后同时遍历直到相遇。这些方法均在O(m+n)的时间复杂度内完成,其中m和n分别为两个链表的长度。
摘要由CSDN通过智能技术生成

输入两个链表,找出它们的第一个公共节点。

如下面的两个链表

在节点 c1 开始相交。

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
解释:这两个链表不相交,因此返回 null。

注意:

如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。
程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。

 方法一:哈希集合法 

判断两个链表是否相交,可以使用哈希集合存储链表节点。

代码语言:cpp

/**
 * 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) {
        unordered_set<ListNode *> q;
        ListNode *tmp = headA;
        while(tmp!=nullptr){    //首先遍历链表headA,并将链表headA中的每个结点加入哈希集合中
            q.insert(tmp);
            tmp = tmp->next;
        }
        tmp = headB;
        while(tmp!=nullptr){    //遍历链表headB,对于遍历到的每个结点,判断该结点是否在哈希集合中
            if(q.count(tmp)){   //如果当前结点在哈希集合中,则后面的结点都在哈希集合中,即从当前结点开始的所有结点都是两个链表的公共结点
                return tmp;
            }
            tmp = tmp->next;
        }
        return nullptr;
    }
};

45 / 45 个通过测试用例

状态:通过

执行用时: 52 ms

内存消耗: 16.8 MB

时间复杂度:O(m+n),其中 m 和 n 是分别是链表 headA 和 headB 的长度。需要遍历两个链表各一次。

空间复杂度:O(m),其中 m 是链表 headA 的长度。需要使用哈希集合存储链表 headA 中的全部节点。

方法二: 双指针法

代码语言:cpp

/**
 * 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) {
        if(headA == nullptr || headB == nullptr){   //只有当链表headA 和headB 都不为空时,两个链表才可能相交
            return nullptr;
        }
        ListNode *pA = headA;
        ListNode *pB = headB;
        while(pA != pB){    //每步操作需要同时更新指针pA和pB(当指针pA和pB指向同一个结点或者都为空时,返回它们指向的结点或者null)
            pA = (pA == nullptr) ? headB : pA->next;    //如果指针pA不为空,则将指针pA移到下一个结点;如果指针pB不为空,则将指针pB移到下一个结点
            pB = (pB == nullptr) ? headA : pB->next;    //如果指针pA为空,则将指针pA移到链表headB的头节点;如果指针pB为空,则将指针pB移到链表headA的头节点
        }
        return pA;
    }
};

45 / 45 个通过测试用例

状态:通过

执行用时: 40 ms

内存消耗: 14.1 MB

时间复杂度:O(m+n),其中 m 和 n 是分别是链表 headA 和 headB 的长度。两个指针同时遍历两个链表,每个指针遍历两个链表各一次。

空间复杂度:O(1)

方法三:快慢指针 

代码语言:cpp 

/**
 * 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) {
        //得到两个链表的长度
        int Alength = GetListLength(headA);
        int Blength = GetListLength(headB);
        int lenthDif = Alength - Blength;   //两个链表长的差值
        ListNode *pheadlong = headA;
        ListNode *pheadshort = headB;
        if(lenthDif < 0){
            lenthDif = Blength - Alength;
            pheadlong = headB;
            pheadshort = headA;
        }
        for(int i = 0; i < lenthDif; i++)   //先让长链表多走(长-短)的距离
            pheadlong = pheadlong->next;
        while(pheadshort != pheadlong){ //长短链表同时遍历,直到遇到第一个公共结点停止
            if((pheadshort != nullptr)&&(pheadlong != nullptr)){
                pheadlong = pheadlong->next;
                pheadshort = pheadshort->next;
            }
            else
                return nullptr;
        }
        return pheadshort;
    }
    unsigned int GetListLength(ListNode* pHead){    //获得链表长度函数
        unsigned int nlength = 0;
        ListNode* pNode = pHead;
        while(pNode != nullptr){
            ++nlength;
            pNode = pNode->next;
        }
        return nlength;
    }
};

 45 / 45 个通过测试用例

状态:通过

执行用时: 36 ms

内存消耗: 14.1 MB

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

For L9L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值