【链表】leetcode面试题 02.07. 链表相交(C/C++/Java/Js)

news2024/11/22 17:41:42

leetcode面试题 02.07. 链表相交

  • 1 题目
  • 2 思路
    • 2.1 思路一
    • 2.2 思路二(强推--5行代码)
  • 3 代码
    • 3.1 C++版本
      • 3.1.1思路一:
      • 3.1.2 思路二
    • 3.2 C版本
      • 3.2.1 思路一
      • 3.2.2 思路二
    • 3.3 Java版本
      • 3.3.1 思路一
      • 3.3.2 思路二
    • 3.4 JavaScript版本
  • 4 总结


1 题目

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:
在这里插入图片描述

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:
在这里插入图片描述

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘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
输出:Intersected at ‘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 。

提示:

listA 中节点数目为 m
listB 中节点数目为 n
0 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]


2 思路

2.1 思路一

简单来说,就是求两个链表交点节点的指针。 这里同学们要注意,交点不是数值相等,而是指针相等。

为了方便举例,假设节点元素数值相等,则节点指针相等。

看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点:
在这里插入图片描述
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
在这里插入图片描述
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。

否则循环退出返回空指针。


2.2 思路二(强推–5行代码)

非常逻辑的一种思路:
打个简单的比方,把这两个链表看作是两条路,两条路有汇合点
在这里插入图片描述
大家结合图可以看出,小蓝走完它对应的路后,再从小绿的起点开始走;
小绿走完它对应的路后,再从小蓝的起点走,这样一定会相遇(图中紫色)

看代码:
如果还在不清楚的可以用自己的两个手指头模拟一下这个过程:

/**
 * 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, *curB = headB;
        while (curA != curB) {
            curA = curA?curA->next:headB;
            curB = curB?curB->next:headA;
        }
        return curA;    //return curB;
    }
};

3 代码


3.1 C++版本

3.1.1思路一:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != NULL) { // 求链表A的长度
            lenA++;
            curA = curA->next;
        }
        while (curB != NULL) { // 求链表B的长度
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) {
            swap (lenA, lenB);
            swap (curA, curB);
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap--) {
            curA = curA->next;
        }
        // 遍历curA 和 curB,遇到相同则直接返回
        while (curA != NULL) {
            if (curA == curB) {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

时间复杂度:O(n + m)
空间复杂度:O(1)


3.1.2 思路二

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *curA = headA, *curB = headB;
        while (curA != curB) {
            curA = curA?curA->next:headB;
            curB = curB?curB->next:headA;
        }
        return curA;    //return curB;
    }
};

3.2 C版本

3.2.1 思路一

ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
    ListNode *l = NULL, *s = NULL;
    int lenA = 0, lenB = 0, gap = 0;
    // 求出两个链表的长度
    s = headA;
    while (s) {
        lenA ++;
        s = s->next;
    }
    s = headB;
    while (s) {
        lenB ++;
        s = s->next;
    }

    // 求出两个链表长度差
    if (lenA > lenB) {
        l = headA, s = headB;
        gap = lenA - lenB;
    } else {
        l = headB, s = headA;
        gap = lenB - lenA;
    }

    // 尾部对齐
    while (gap--) l = l->next;
    // 移动,并检查是否有相同的元素
    while (l) {
        if (l == s) return l;
        l = l->next, s = s->next;
    }

    return NULL;
}

3.2.2 思路二

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *curA = headA, *curB = headB;
        while (curA != curB) {
            curA = curA?curA->next:headB;
            curB = curB?curB->next:headA;
        }
        return curA;    //return curB;
}

3.3 Java版本

3.3.1 思路一

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA;
        ListNode curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != null) { // 求链表A的长度
            lenA++;
            curA = curA.next;
        }
        while (curB != null) { // 求链表B的长度
            lenB++;
            curB = curB.next;
        }
        curA = headA;
        curB = headB;
        // 让curA为最长链表的头,lenA为其长度
        if (lenB > lenA) {
            //1. swap (lenA, lenB);
            int tmpLen = lenA;
            lenA = lenB;
            lenB = tmpLen;
            //2. swap (curA, curB);
            ListNode tmpNode = curA;
            curA = curB;
            curB = tmpNode;
        }
        // 求长度差
        int gap = lenA - lenB;
        // 让curA和curB在同一起点上(末尾位置对齐)
        while (gap-- > 0) {
            curA = curA.next;
        }
        // 遍历curA 和 curB,遇到相同则直接返回
        while (curA != null) {
            if (curA == curB) {
                return curA;
            }
            curA = curA.next;
            curB = curB.next;
        }
        return null;
    }

}

3.3.2 思路二

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode curA = headA, curB = headB;
        while (curA != curB) {
            if (curA != null) curA = curA.next;
            else curA = headB;
            if (curB != null) curB = curB.next;
            else curB = headA;
        }
        return curA;    //return curB;
    }
}

3.4 JavaScript版本

var getListLen = function(head) {
    let len = 0, cur = head;
    while(cur) {
       len++;
       cur = cur.next;
    }
    return len;
}
var getIntersectionNode = function(headA, headB) {
    let curA = headA,curB = headB,
        lenA = getListLen(headA),   // 求链表A的长度
        lenB = getListLen(headB);  
    if(lenA < lenB) {       // 让curA为最长链表的头,lenA为其长度
    
        // 交换变量注意加 “分号” ,两个数组交换变量在同一个作用域下时
        // 如果不加分号,下面两条代码等同于一条代码: [curA, curB] = [lenB, lenA]
        
        [curA, curB] = [curB, curA];
        [lenA, lenB] = [lenB, lenA];
    }
    let i = lenA - lenB;   // 求长度差
    while(i-- > 0) {       // 让curA和curB在同一起点上(末尾位置对齐)
        curA = curA.next;
    }
    while(curA && curA !== curB) {  // 遍历curA 和 curB,遇到相同则直接返回
        curA = curA.next;
        curB = curB.next;
    }
    return curA;
};


4 总结

总结就是,好好把两个思路的方法看懂。会有很大帮助,尤其是第二个思路,非常巧妙。


By --Suki 2023/1/12

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【进阶C语言】动态内存管理+柔性数组

文章目录1.动态内存的开辟内存的布局内存池内存碎片内存泄漏2.动态内存函数malloc功能函数calloc功能函数realloc功能函数开辟时遇到的两种情况free功能函数3.  建议4.柔性数组特性&#xff1a;定义使用优点1.动态内存的开辟 内存的布局 我们常用的内存开辟函数都是在堆区开辟…

几个特殊的运算符重载(前置\后置++、前置\后置--、<<、>>)

几个特殊的运算符重载 概念 运算符重载是一个非常重要的概念&#xff0c;在运算符重载中我们可以重新定义 运算符 的具体含义&#xff0c;一个运算符重载函数的定义是 T operator 运算符 (参数) &#xff0c;对于运算符重载有以下重点。 以下讨论的都是双操作数的运算符 双操…

uniapp: 本应用使用HBuilderX x.x.xx 或对应的cli版本编译,而手机端SDK版本是 x.x.xx。不匹配的版本可能造成应用异常。

目录场景与问题描述&#xff1a;原因分析&#xff1a;解决方案&#xff1a;方案一&#xff1a;更新HbuilderX版本方案二&#xff1a;设置固定的版本方案三&#xff1a;忽略版本&#xff08;不推荐&#xff09;场景与问题描述&#xff1a; 项目场景&#xff1a;示例:通过使用Hb…

【docker13】Dockfile

1.Dockerfile是什么 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本 自己的理解是&#xff1a;将多次繁琐的commit命令构成一个Dockerfile文本然后一次性执行完成&#xff0c;可以简化复杂程度 Dockerfile官网 构建三部…

Docker部署 registry

系列文章目录 Docker部署 registry Docker搭建 svn Docker部署 Harbor Docker 部署SQL Server 2017 Docker 安装 MS SqlServer Docker部署 Oracle12c Docker部署Jenkins Docker部署 registry系列文章目录前言一、registry搭建二、使用步骤1. pull registry2. run image3. 验证…

通过gcloud创建Google Kubernetes Engine(GKE)并通过kubectl访问

1 简介 GKE(Google Kubernetes Engine)是一个K8s平台&#xff0c; 我们可以使用gcloud来创建GKE集群。在开始之前&#xff0c;可以查看&#xff1a;《初始化一个GCP项目并用gcloud访问操作》。 2 创建GKE集群 2.1 打开API 在创建集群之前&#xff0c;需要打开Google API&am…

图扑数字孪生水利工程,助力水资源合理利用

前言从大禹治水到三峡大坝的建造&#xff0c;人类为控制和调配自然界的地表水和地下水&#xff0c;修建了许多的水利工程。对水资源进行了广泛的开发利用&#xff0c;诸如农业灌溉、工业和生活用水、水力发电、航运、港口运输、淡水养殖、旅游等。将图扑软件与 GIS、粒子仿真、…

力扣算法(Java实现)—字符串入门(9题)

文章目录1.反转字符串2.整数反转3.字符串中的第一个唯一字符4.有效的字母异位词5.验证回文串6.字符串转换整数 (atoi)7.实现strStr()8.外观数列9.最长公共前缀&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e; 更多资源链接&#xff0c;欢迎访问作者gitee仓…

STM32那些事

STM32芯片型号命名方式STM32开发板的GPIO编程GPIO的函数调用顺序&#xff1a;&#xff08;1&#xff09;使能GPIO时钟&#xff1a;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);第一个参数是GPIO对象&#xff0c;第二个参数是枚举使能&#xff08;2&#xff09;初始化…

Python - 数据容器tuple(元组)

目录 定义元组 元组的相关操作 修改元组 元组的删除 转换为元组tuple 定义元组 元组同列表一样&#xff0c;都是可以封装多个、不同类型的元素在内。 但最大的不同点在于&#xff1a;元组一旦定义完成&#xff0c;就不可修改 元组定义&#xff1a;定义元组使用小括号&…

第二章.线性回归以及非线性回归—梯度下降法

第二章.线性回归以及非线性回归 2.5 梯度下降法 1.流程&#xff1a; 初始化θ0,θ1 不断改变θ0,θ1&#xff0c;直到J(θ0,θ1)到达一个全局最小值或局部极小值 2.图像分析&#xff1a; 1).图像层面分析代价函数&#xff1a; ①.红色区域表示代价函数的值比较大&#xff0…

leetcode 1443.Minimum Time to Collect All Apples in a Tree(收集苹果所需最短时间)

给出节点个数n, edges是连接的边&#xff0c;[a,b]是连接的两个顶点。 hasApple表示第 i 个顶点上是否有苹果。 走一条边需要耗时1s, 从顶点0出发&#xff0c;最后回到顶点0, 问收集所有苹果所需最短的时间。 思路&#xff1a; &#xff08;1&#xff09;DFS 可以把问题简化…

pdf文档控件Aspose.PDF for .NET 授权须知

Aspose.PDF是一款高级PDF处理API&#xff0c;可以在跨平台应用程序中轻松生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现&#xff0c;保护和打印文档。无需使用Adobe Acrobat。此外&#xff0c;API提供压缩选项&#xff0c;表创建和处理&#xff0c;图形和图像功能&…

[ 解决报错篇 ] tomcat 执行 startup.bat 文件报错 -- tomcat 启动失败(安装 java 环境并配置环境变量)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

Linux——팔 gdb部分基础知识以及操作系统的初级理解

文章目录一、gdb部分基础知识1、打断点2、逐语句过程3、监视&#xff0c;内存4、部分功能指令二、进程概念的初级理解1、冯-诺依曼体系结构2、操作系统的初级理解一、gdb部分基础知识 承接上一篇。 1、打断点 放一下全部代码 1 #include <stdio.h>2 3 int ADD(int n)4…

vs2017调试ffprobe源码

鄙人之前写过vs2017调试ffmpeg源码 现在由于需要分析视频文件里面的具体帧情况&#xff0c;需要用到ffprobe工具&#xff0c;为此本篇博客搭建vs2017工程&#xff0c;可以调试ffprobe&#xff0c;ffprobe比ffmpeg简单很多。 首先找到ffmpeg的编译目录&#xff0c;将下列三个文…

结构体内存对齐与位段详解

文章目录前言一、内存对齐1.内存对齐的规则2. 内存对齐的具体实例与运用3.为什么会有内存对齐&#xff1f;4.修改默认对齐数二、位段1.什么是位段2.位段的例子3.位段的优缺点结语前言 学习了结构体&#xff0c;你会算结构体的占用字节数吗&#xff0c;许多人恐怕摇头&#xff0…

ArcGIS分式、假分式标注

解说一下 ArcGIS中不同标注 直接标注语句分享见文后 分式标注 假分式标注 二、标注语句与视频教学 ArcGIS分数、假分数标注获取语句地址&#xff1a; ArcGIS分式、假分式标注&#xff01;标注语句直接分享 推荐学习 ArcGIS之模型构建器&#xff08;ModelBuilder&#xff0…

初级通讯录的实现详细攻略

我们设计的要求存储多少个人的信息我们使用宏&#xff0c;这样方便修改建立三个文件先从主函数入手为了持续多次&#xff0c;实现操作&#xff0c;我们利用循环&#xff0c;循环次数未知&#xff0c;我们只能选择while循环&#xff0c;do-while循环&#xff0c;我们始终要进行一…

ORB-SLAM2 --- LoopClosing::Run 回环检测线程解析

目录 1.函数作用 2.code 3.函数解析 3.1 查看是否有待处理的回环关键帧LoopClosing::CheckNewKeyFrames 3.2 检测闭环LoopClosing::DetectLoop 3.3 计算当前关键帧和上一步闭环候选帧的Sim3变换 3.4 闭环矫正 1.函数作用 回环检测&#xff0c;又称闭环检测&#xff0c…