【链表】LC 160.相交链表

文章目录

前言

本专栏文章为《LeetCode 热题 100》的刷题题解,相关内容如有侵权,立即删除。

一、题目

1、原题链接

160.相交链表

2、题目描述

二、个人思路整理

1、思路分析

双指针解法

这个解法有点妙,说实话理解不是很透彻,根据自己的理解梳理一下。

  1. 由于想要达到O(1)的空间复杂度,不能开辟额外空间;
  2. 本质上还是消除两个链表的长度差(由于链表在相交节点前走过的节点数是不同的,所以不能直接同时遍历来找相交的节点),从而达到一次遍历找到交点(当然也可以先计算链表的长度差,然后让长链表先走长度差距离,然后再一起向后走,第一个相同的节点即为交点);
  3. 我的理解是:如果要同时一次同时遍历就能找到交点,就必须把交点前的距离(经过的节点数补齐(补齐即可,不需要管补充的这段距离是否存在相交节点,如果确实存在,在后续遍历中由于补充的距离不同,所以不可能是在同时遍历中的第一个相同节点),这样一次同时遍历,当遇到的第一个相同节点即为相交节点);
  4. 本质上我认为是3这种理解。具体举例:
  • 如果链表A长度为a、链表B长度为b,相交的部分长度为c(可以把从A从B链表开始遍历的距离各想成一条直线,直线最后部分就是c,而直线前半部分存在不相等的情况需要补全)
  • 若要补全前面的长度差,就可以让链表A多走一个b距离,让链表B多走一个a距离,这样就相当于A走了a+b,而B走了b+a,由于只是补在前面,后面的共有部分没有变,所以当前面走完相同距离遇到的第一个相同的点即为交点。
  1. 所以,最终的解法:
    两链表同时遍历:
    A链表先遍历一遍A,若遍历完则继续遍历B;
    B链表先遍历一遍B,若遍历完则继续遍历A;
    当遍历遇到第一个相同节点即为交点。

下面为大模型给出的解释(参考防遗忘)

为什么这个方法有效?(数学原理解析)设:

  • 链表 A 独有的长度为 a a a
  • 链表 B 独有的长度为 b b b
  • 两链表公共(相交)部分的长度为 c c c
    那么:
  • 链表 A 的总长度为 a + c a + c a+c
  • 链表 B 的总长度为 b + c b + c b+c

当指针 pA 走完 A 再走 B 时,到达交点的总路程是: a + c + b a + c + b a+c+b

当指针 pB 走完 B 再走 A 时,到达交点的总路程是: b + c + a b + c + a b+c+a

因为 a + c + b = b + c + a a + c + b = b + c + a a+c+b=b+c+a,所以两个指针走过的总路程完全一致!这就消除掉了两条链表前段长度不一样的差值。

  • 如果不相交(即 c = 0 c = 0 c=0):pA 走了 a + b a + b a+b 步后变为 nullptr,pB 走了 b + a b + a b+a 步后也变为 nullptr,此时 pA == pB == nullptr,完美统一了逻辑。

非双指针解法(哈希表)

遍历链表A,将元素放置到哈希表中,然后从头节点开始依次遍历链表B,遍历到第一个的存在于哈希表中的节点,即为相交节点,否则不相交。

2、解题代码

双指针解法

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) {
        ListNode *pa = headA;
        ListNode *pb = headB;
        // 当指向同一节点则结束
        while (pa != pb) {
            pa = (pa == nullptr) ? headB : pa->next; //当遍历完A遍历B
            pb = (pb == nullptr) ? headA : pb->next; //当遍历完B遍历A
        }
        return pa;
    }
};

非双指针解法(哈希表)

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*> s;
        ListNode *tmp = headA;
        while(tmp != nullptr) {
            s.insert(tmp);
            tmp = tmp->next;
        } 
        tmp = headB;
        while(tmp != nullptr) {
            if(s.count(tmp)) {
                return tmp;
            }
            tmp = tmp->next;
        }
        return nullptr;
    }
};

三、知识风暴

  • NULL与nullptr
    • C++11及以上一律使用nullptr表示空指针(nullptr:C++11 引入的关键字,强类型字面量;实际类型为std::nullptr_t 类型的常数);
    • 纯C语言或C++98以前才继续使用NULL(NULL:宏定义,通常被定义为整数0 或 (void*)0;实际类型为整型(int / long))
相关推荐
蛋蛋的就会顺顺的4 小时前
hot100——矩阵
java·数据结构·算法·leetcode·力扣
学代码的CJY5 小时前
从代码出发理解时间复杂度与空间复杂度
数据结构·算法
字节跳动的猫6 小时前
LikeShop 积分体系全链路改造:获取规则、订单抵扣与积分商品兑换二开
运维·数据结构
上位机妹子7 小时前
C 语言 数组删除指定元素(快慢指针法)
c语言·数据结构·算法
C++ 老炮儿的技术栈9 小时前
有符号变量与无符号变量的区别
服务器·开发语言·前端·数据结构·c++·算法·c
keyipatience10 小时前
跳表详细解析
开发语言·数据结构·c++·算法·跳表
小小、码农11 小时前
〖Linux文件系统〗:彻底打通文件 IO 全链路
linux·开发语言·数据结构·c++·系统
INGNIGHT12 小时前
1897 · 会议室 3(扫描线)
数据结构·算法
All for pursuit.12 小时前
【贪心-2】253.会议室 II
数据结构·c++·算法·leetcode
Logic10116 小时前
C语言/数据结构字符串题解:按字符类型重排(数字→字母→符号)——三趟扫描稳定分区
c语言·数据结构·字符串·时间复杂度·计数排序·算法题·ascii