LeetCode/NowCoder-链表经典算法OJ练习4

news2024/11/18 18:42:13

·人的才华就如海绵的水,没有外力的挤压,它是绝对流不出来的。流出来后,海绵才能吸收新的源泉。💓💓💓

目录

说在前面

题目一:环形链表

 题目二:环形链表 II

题目三:随机链表的复制

SUMUP结尾


说在前面

 dear朋友们大家好!💖💖💖我们又见面了,接着上一个篇目,我们接着继续练习有关链表的面试题、OJ题,希望大家和我一起学习,共同进步~

 👇👇👇

友友们!🎉🎉🎉点击这里进入力扣leetcode学习🎉🎉🎉


​以下是leetcode题库界面:

 👇👇👇

🎉🎉🎉点击这里进入牛客网NowCoder刷题学习🎉🎉🎉
​以下是NowCoder题库界面:

​​

 ​​

题目一:环形链表

题目链接:141. 环形链表 - 力扣(LeetCode)

题目描述:

题目分析:

 思路:快慢指针法。创建快慢指针fast、slow,快指针每次走两步,慢指针每次走一步,如果fast追击上slow(即fast最终等于slow),则链表带环,否则不带环。

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
    ListNode* slow = head, *fast = head;//创建快慢指针fast、slow
    while(fast && fast->next)//fast走两步,slow走一步
    {
        slow = slow->next;
        fast = fast->next->next;

        if(slow == fast)//fast追击上slow,则链表带环
            return true;
    }
    return false;
}

思考下列问题:

 当然,虽然这道题比较简单,但是有些问题我们还是需要搞清楚:

1、为什么一定会相遇,有没有可能会错过,永远追不上?请证明。

答:不会错过。

证明:

假设slow进环时,fast和slow之间的距离为d,那么在fast追击slow的过程中,距离变化为d、d-1、d-2、d-3、...、2、1、0,最终会减到0。当距离为0时,fast追上slow,也就是每追击一次距离d减小1,所以一定能追上。

2、slow一次走1步,fast走3步可以吗?4步、5步、n步呢?请证明。

答:不稳定,有可能很久都追不上。

证明:

我们先考虑slow走1步,fast走3步的情况,剩下的都是如法炮制。同样假设fast和slow之间的距离为d,那么在fast追击slow的过程中,距离变化为d、d-2、d-4、d-6、...,此时就需要讨论d的奇偶性。如果d是偶数,那么d可以减到0,即fast最终会追上slow,但是如果d是奇数,那么d不是2的倍数,它会错过slow并新的距离为C-1(假设C是环的节点数),进入新一轮的追击过程,直到他们两的距离是2的倍数,此时这一轮就可以追上了。

永远追不上的条件:同时存在C是偶数且距离d(或N)为奇数,那么就会永远追不上。 

那是否真的会有这种情况呢?我们需要寻找C和N之间的关系:

证明:

假设slow进环时,fast和slow的距离是N,带环部分的节点数为C,链表不带环部分的节点数为L,slow进环时fast已经在环中转了x圈,则有:

slow走的距离是:L

fast走的距离是:L + xC + (C - N)

由于fast走的距离是slow的三倍,则有:3L = L + xC + C - N

化简得到:2L = (x + 1)C - N

显然C为偶数且N为奇数不能同时成立,也就是说,fast最终总是会追击上slow。

 ​​

 题目二:环形链表 II

题目链接:142. 环形链表 II - 力扣(LeetCode)

题目描述:

题目分析:

 思路1:快慢指针法。用题目一中的方法找到fast追击上slow的节点,记为meet,再将head记为cur,让meet和cur同时走,它们会再第一个相交节点相遇,即入环的第一个节点。

那为什么会在第一个相交节点相遇呢?

解:

假设fast追上slow时的节点,即meet节点,逆时针距离入环的第一个节点的距离为N,不带环部分节点数为L,带环部分节点数为C,则有:

slow走的距离是:L + N(fast一次走2步,在一圈内一定能追上)

fast走的距离是:L + xC + N

又因为fast走的距离是slow的两倍,则有:2(L + N) = L + xC + N

化简得到:L = xC- N

显然随着x的变化,指针fast在环内距离入环的第一个节点的距离f(x)是一个周期函数,周期为T = 1,那么L(x)也是关于x的周期函数,周期T = 1。

所以:L = C - N(就比如sinπ、sin3π和sin5π都是相等的)

由此得到不带环部分的节点数等于meet距离入环的第一个节点的节点数相等,所以它们以相同速度移动,一定会再第一个相交节点相遇。

代码如下: 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode* detectCycle(struct ListNode* head) {
    ListNode* slow = head, * fast = head;//创建快慢指针
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)//fast追上slow
        {
            ListNode* meet = slow;
            ListNode* cur = head;
            while (meet != cur)//meet和cur相遇,则为第一个相交节点
            {
                meet = meet->next;
                cur = cur->next;
            }
            return meet;
        }
    }
    return NULL;//没有相交节点则返回NULL
}

思路2:先用思路1的方法找到meet,然后将meet->next设置为newhead,然后让环断裂,使其转化为相交链表找第一个相交节点(上个篇目解析过这类题)。 

这个思路可能更好想,但是代码却要更加复杂一些。

代码如下: 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
//相交单链表寻找第一个相交节点
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
	ListNode* cur1 = headA, * cur2 = headB;
	int lenA = 0;
	int lenB = 0;
	while (cur1->next)//统计链表A的长度
	{
		lenA++;
		cur1 = cur1->next;
	}
	while (cur2->next)//统计链表B的长度
	{
		lenB++;
		cur2 = cur2->next;
	}
	if (cur1 != cur2)//判断是否有交点
		return NULL;
    //假设法,设置长短链表
	ListNode* LongList = headA, * ShortList = headB;
	if (lenA < lenB)
	{
		LongList = headB;
		ShortList = headA;
	}
	int gap = abs(lenA - lenB);//两链表节点差值
	while (gap--)//让长的先走差值的步数
	{
		LongList = LongList->next;
	}
	while (LongList != ShortList)//让两链表一起走,第一个相等的就是交点
	{
		LongList = LongList->next;
		ShortList = ShortList->next;
	}
	return LongList;
}
//带环链表寻找第一个相交节点
struct ListNode *detectCycle(struct ListNode *head) {
    ListNode *slow = head, *fast = head;//创建快慢指针
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
           ListNode* meet = slow;
           ListNode* newhead = meet->next;//新单链表的头
           meet->next = NULL;//尾部置为NULL
           ListNode* cur = getIntersectionNode(newhead, head);
           return cur;
        }
    }   
    return NULL;
}

第一个函数我们在上一篇目中有详细解析,这里直接CV就可以了。 虽然能够通过,但是修改了链表,本质上不符合题目要求了,但是也是个值得思考的思路。

 ​​

题目三:随机链表的复制

题目链接:138. 随机链表的复制 - 力扣(LeetCode)

题目描述:

注:深拷贝:拷贝一个值个指向和当前链表一模一样的链表。

题目分析:

 思路:快先将新的节点交错插入到原随机链表中,然后完成设置random后再将新的节点尾插到新链表newhead中。

这道题目创建节点其实不难,难就难在random是随机的,应该如何处理random的指向是这道题目的难点。我们如果错位插入,就可以得到新节点和旧链表之间的关系,那么新的节点的random指针都会指向插入了新节点的链表的random的next指针(空指针除外)。

比如,上面原来的第二个节点的random指向了第一个节点,那么新的第二个节点的random就指向第一个节点的next节点。

代码如下:

 

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */
typedef struct Node Node;
struct Node* copyRandomList(struct Node* head) {
    Node* cur = head;
    while (cur)//创建新节点并错位插入旧节点
    {
        Node* copy = (Node*)malloc(sizeof(Node));
        copy->val = cur->val;
        copy->next = cur->next;
        cur->next = copy;
        cur = copy->next;
    }
    cur = head;
    while (cur)//设置random指针的指向
    {
        Node* copy = cur->next;
        if (!cur->random)
            copy->random = NULL;
        else
        {
            copy->random = cur->random->next;
        }
        cur = copy->next;
    }
    cur = head;
    //创建新链表
    Node* newhead = (Node*)malloc(sizeof(Node));
    Node* newtail = newhead;
    while (cur)//将新节点尾插到新链表
    {
        Node* copy = cur->next;
        newtail->next = copy;
        newtail = newtail->next;
        cur->next = copy->next;//恢复原链表
        cur = copy->next;
    }
    if (newtail)
        newtail->next = NULL;
    return newhead->next;
}

这道题目不论是思路还是代码书写都很有难度,如果你看了我的讲解能够独立地写出上面代码(不一定非要和我一样),那么恭喜你,在当前阶段你的链表已经过关了!

 

SUMUP结尾

数据结构就像数学题,需要刷题才能对它有感觉。之后还会更新数据结构相关的练习题、面试题,希望大家一起学习,共同进步~

如果大家觉得有帮助,麻烦大家点点赞,如果有错误的地方也欢迎大家指出~

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

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

相关文章

json web token及JWT学习与探索

JSON Web Token&#xff08;缩写 JWT&#xff09;是目前最流行的跨域认证解决方案 作用&#xff1a; 主要是做鉴权用的登录之后存储用户信息 生成得token(令牌)如下 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjg3Njc0NDkyLCJleHAiOjE2ODc3NjA4OTJ9.Y6eFG…

视创云展「VR直播」是什么?有哪些功能和应用场景?

视创云展「VR直播」通过“3D沉浸式展厅直播高互动感”的创新玩法&#xff0c;使企业随时随地举办一场低成本、高互动、能获客的元宇宙直播活动成为可能。「VR直播」能实现3D展厅内VR场景漫游&#xff0c;更结合音视频交互、同屏互动等新功能&#xff0c;为用户带来更沉浸的虚拟…

OpenStack创建云主机——超级详细步骤

四、创建云主机 一台云主机成功创建或启动需要依赖OpenStack中的各种虚拟资源&#xff0c;如CPU、内存、硬盘等。如果需要云主机丽娜姐外部网络&#xff0c;还需要网络、路由器等资源。如果需要外部网络访问云主机&#xff0c;那么还需要配置浮动IP。因此&#xff0c;在创建云主…

HiWoo Box工业网关

在科技飞速发展的今天&#xff0c;工业领域正迎来智能化变革。在这场变革中&#xff0c;工业网关作为连接工业设备与远程控制中心的桥梁&#xff0c;发挥着至关重要的作用。HiWoo Box网关凭借其卓越的性能和广泛的应用场景&#xff0c;为工业领域带来了全新的智慧化解决方案。 …

华为WLAN实验继续-2,多个AP如何部署

----------------------------------------如果添加新的AP&#xff0c;如何实现多AP的服务----------- 新增加一个AP2启动之后发现无法获得IP地址 在AP2上查看其MAC地址&#xff0c;并与将其加入到AC中去 打开AC&#xff0c;将AP2的MAC加入到AC中 sys Enter system view, re…

常见开源蜜罐系统

蜜罐系统&#xff08;Honeypot&#xff09;在信息安全领域中是一种被广泛使用的技术&#xff0c;旨在吸引和诱导黑客入侵&#xff0c;从而获取和分析攻击者的行为和手段。以下是一些常见的蜜罐系统的介绍&#xff1a; HFish开源蜜罐系统 特点&#xff1a; 多功能&#xff1a;支…

[数据集][目标检测]航空发动机缺陷检测数据集VOC+YOLO格式291张4类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;291 标注数量(xml文件个数)&#xff1a;291 标注数量(txt文件个数)&#xff1a;291 标注类别…

Java 使用WebMagic爬取网页(简单示例)

框架简介 WebMagic是一个基于Java的开源网络爬虫框架&#xff0c;它提供了很多简单易用的API接口&#xff0c;可以帮助使用者快速构建出高效、可扩展的网络爬虫程序&#xff0c;WebMagic由四个组件(Downloader、PageProcessor、Scheduler、Pipeline)构成&#xff0c;核心代码非…

Android制作.9图

需求背景&#xff1a;android 启动图变形 开发语言&#xff1a;uni-app&#xff0c;uni-app官网 俗语曰&#xff1a;授人以鱼不如授人以渔 原创地址&#xff1a;Android制作.9图 语雀 一.工具 使用android studio&#xff0c;因为android studio已经集成.9.png制作工具&a…

AI视频教程下载:零基础学会DALL-E 、Midjourney、Microsoft Designer、Adobe Firefly

学完本课程会得到什么&#xff1a; 掌握ChatGPT、DALL-E 2、Midjourney、Microsoft Bing Chat、Microsoft Designer和Adobe Firefly&#xff0c;全面理解生成性AI及其应用 了解OpenAI及其在生成性AI领域的尖端研究 理解提示工程的重要性以及它如何帮助产生更好的输出和数据 …

计算机二级Access操作题总结——基本操作

基础操作题 设置主键 例&#xff1a;将“线路”表中的“线路ID”字段设置为主键 ①右键单击“线路”表&#xff1b; ②单击【设计视图】&#xff1b; ③鼠标指到表的第一行→“线路ID”处&#xff0c;右键单击&#xff1b; ④单击【主键】 设置有效性规则 例&#xff1a;设…

【UnityUI程序框架】The PureMVC Framework核心你会用吗

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

【计算机视觉 Mamba】MambaOut: Do We Really Need Mamba for Vision?

MambaOut: Do We Really Need Mamba for Vision? 在视觉任务上我们需要Mamba吗? 论文地址 代码地址 知乎解读&#xff1a;王牌飞行员申请出战&#xff01; 知乎解读&#xff1a;Mamba 模型解读 (一)&#xff1a;MambaOut&#xff1a;在视觉任务中&#xff0c;我们真的需要 …

【Go语言入门学习笔记】Part3.指针和运算符、以及基本输入

一、前言 仍然好多和C语言类似&#xff0c;计算机的学生应该是很容易入门这一环节&#xff0c;我还在最后的输入中看到了一些些Java输入的影子&#xff0c;而自动的变量类型推断更是有Python那个味道&#xff0c;正可谓几百家之所长了。 二、学习代码 package mainimport (&q…

【已解决】使用StringUtils.hasLength参数输入空格仍然添加成功定价为负数仍然添加成功

Bug情景 今天在做功能测试时&#xff0c;发现使用使用StringUtils.hasLength&#xff08;&#xff09;方法以及定价为负数时&#xff0c;添加图书仍然成功 思考过程 0.1 当时在做参数检验时用了spring提供的StringUtils工具包&#xff0c;百度/大数据模型说&#xff1a; 0.2…

详解Spring MVC

目录 1.什么是Spring Web MVC MVC定义 2.学习Spring MVC 建立连接 RequestMapping 注解介绍及使用 获取单个参数 获取多个参数 获取普通对象 获取JSON对象 获取基础URL参数 获取上传文件 获取Header 获取Cookie 获取Session 总结 1.什么是Spring Web MVC 官⽅对于…

私域如何高效管理多微信并实现聚合聊天?

在私域经营中&#xff0c;管理多个微信号是一项具有挑战性的任务。为了提高工作效率&#xff0c;辅助工具成为必不可少的一部分。而个微管理系统将为大家带来高效的多微信号管理体验&#xff0c;让大家能够更好地聚合聊天。 首先&#xff0c;个微管理系统提供了一个统一的界面…

PP-ShiTu V2商品识别系统(01.简介)

PP-ShiTu V2商品识别系统&#xff08;01.简介&#xff09; PP-ShiTuV2 是基于 PP-ShiTuV1 改进的一个实用轻量级通用图像识别系统&#xff0c;由主体检测、特征提取、向量检索三个模块构成&#xff0c;相比 PP-ShiTuV1 具有更高的识别精度、更强的泛化能力以及相近的推理速度*…

5.28_Java语法_运算符,接收键盘数据

1、运算符 具体应用同我C语言操作符详解博客相同,另有补充会直接写 1.1、基本的算术运算符、符号做连接符 CSDN 具体应用同我C语言操作符详解博客相同 符号做连接符&#xff1a; ""符号与字符串运算连用的时候是用作连接符的&#xff0c;其结果依然是一个字符串…