带环链表和链表的复制,检验你链表的学习情况

前言:带环链表是链表中的经典问题,需要一定的数理思维,一定要掌握其来龙去脉,这样可以加深理解。本文主要讲解一下个人对带环链表的理解。

带环链关的OJ题

1.判断链表是否带环

题目:

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos 为 -1 或者链表中的一个 有效索引 。

思路:

这题判断链表是否带环,方法这里用的是快慢指针的方法,慢指针走一步,快指针走两步。如果快慢指针可以相遇,那么就说明带环;如果快指针的指向空或指针的下一个节点为空,那么就说明,遍历完链表,就是不带环。带环的链表是走不出循环的。

代码如下:

typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
    ListNode* fast = head;//快指针
    ListNode* slow = head;//慢指针
    while(fast&&fast->next)//两个条件不能颠倒顺序
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast==slow)
        {
            return true;
        }       
    }
    return false;
}

为什么fast和slow一定会相遇呢? 

此时,是fast追赶slow,他们俩的距离为N,并且fast每次走两步,slow每次走一步,那么每循环一次,fast与slow的距离就会减1,直到两个指针的距离为0. 

拓展:

带环链表,如果fast是slow的三倍,两个指针是否会一定相遇?四倍呢?……?

 三倍的情况:

fast一次走三步,slow一次走三步,fast和slow每走一步,他俩的距离就减少2.

如果N为偶数,那么fast和slow一定相遇。

如果N为奇数,那么slow和fast会错过,错过时,slow与fast的距离为-1,设整个圈的长度为C, 

此时的-1可以理解为fast与slow的距离为C-1,

如果C-1为偶数,C为奇数那么fast和slow一定可以相遇:

那么C-1为奇数,C为偶数,那么fast和slow不会相遇,那么fast和slow再次错开,并且距离仍为C-1,仍不可能相遇,循环往复,fast和slow就一定不相遇。

但事实上一定不相遇的条件(N为奇数,C为偶数)是否能同时成立呢?

以下是一段证明:

结论:当fast为slow的三倍时,一定相遇。

 四倍:

1.如果N为3的倍数,那么一定相遇;

2.如果N%3==1,那么fast与slow错过后两个人的距离为-2,即为C-2,

   a.如果(C-2为)%3==0时,一定相遇;

  b. 如果(C-2)%3==1时那么一定不相遇;

  c.如果(C-2)%3==2时,那么两个错过后的距离为C-1,(C-1)%3==2,之后就是两个的距离一     直为C-1,那么一定不相遇。

3.如果N%3==2,那么fast与slow错过后两个的距离为-1,即为C-1,

    a.如果C-1为偶数时,一定相遇;

    b.如果(C-1)%3==1,两个人错过后的距离为C-2,(C-2)%3==0

    c.如果(C-2)%3==2,两个错过后的距离为C-1,(C-1)%3==1,之后就是(C-1)和(C-2)循      环往复,所以时一定不相遇。

根据以上的讲解可以自己推导其他的几种情况。

 

2.找带环链表的环的入口

题目:

142. 环形链表 II

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 104] 内
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

思路1:

通过快慢指针,找到slow与fast相遇的位置,并定义一个新指针为meet,让meet和head同时走一步,直到meet和head相遇,相遇的位置就是环的入口。

代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast&&fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //slow与fast相遇
        if(slow==fast)
        {
            ListNode* meet = slow;
            while(meet!=head)
            {
                meet = meet->next;
                head = head->next;
            }
            return head;
        }
    }
    return NULL;
}

证明为什么meet和head同时走相遇的位置为环入口的位置?

思路2:

将链表转换为相交链表,变成处理相交链表找交点的问题。

相交链表的链接:这个的题解可以找一下我以前的博客:这里就不多讲了,这个题如果以前写过了,直接复制粘贴就可以了,那么这题就会非常好写。

160. 相交链表

 

 

代码:

/**
 * 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* ptailA = headA;
    ListNode* ptailB = headB;
    int lenA = 1;
    int lenB = 1;
    while(ptailA)
    {
        ptailA = ptailA->next;
        lenA++;
    }
    while(ptailB)
    {
        ptailB = ptailB->next;
        lenB++;
    }
    if(ptailA!=ptailB)
    {
        return NULL;
    }
    //假设法
    ListNode* listlong = headA;
    ListNode* listshort = headB;
    int k = fabs(lenA-lenB);//两个链表长度的差
    if(lenB>lenA)
    {
        listlong = headB;
        listshort = headA;
    }
    while(k--)
    {
        listlong = listlong->next;
    }
    while(listlong)
    {
        if(listlong==listshort)
        {
            return listlong;
        }
        listlong = listlong->next;
        listshort = listshort->next;
    }
    return NULL;
}
struct ListNode *detectCycle(struct ListNode *head) {
    ListNode* fast = head;
    ListNode* slow = head;
    while(fast&&fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast)
        {
            ListNode* newhead = slow->next;
            slow->next = NULL;       
            return getIntersectionNode(newhead,head);
        }
    }
    return NULL;
}

你链表的试金石:随机链表的复制

题目:

138. 随机链表的复制

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝解释一下什么是深拷贝:拷贝指针指向和当前链表一模一样的链表。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。

你的代码  接受原链表的头节点 head 作为传入参数。

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

提示:

  • 0 <= n <= 1000
  • -104 <= Node.val <= 104
  • Node.random 为 null 或指向链表中的节点。

题目要求的简单说明,除去random复制链表剩余的部分实现很简单,这题的重点主要是新链表random的指向如何和原链表保持一致。就根据现在所学的知识,以下方式相对简单。

 思路:

1.拷贝原链表的节点并将其插入相应的原链表的节点的后面,这里的插入相当于链表中的随机插入。

2.处理random,使复制链表的random指向与原链表一致。

3.将现在链表中的复制链表部分尾插到新链表中,并恢复原链表(原链表恢不恢复,都可以,主要根据题目吧,如果题过不来了就恢复,过得了就恢不恢复都可以)

以例三为例用图片演示一下思路:

代码:

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */

struct Node* copyRandomList(struct Node* head) {
    struct Node*cur = head;
    //拷贝原链表并插入相应节点的后面
	while(cur)
    {
        struct Node*copy = (struct Node*)malloc(sizeof(struct Node));
        if(copy==NULL)
        {
            return NULL;
        }
        copy->val = cur->val;
        //注意以下两句不能交换位置 
        copy->next = cur->next;
        cur->next = copy;
        cur = copy->next;
    }
    //处理random
    cur = head;
    while(cur)
    {
        struct Node* copy = cur->next;
        //如果原链表的该节点的random指向NULL,那么复制节点的random也指向
        if(cur->random==NULL)
        {
            copy->random = NULL;
        }
        else
        {
            //这一句是整个题的思路的重点:此时链表的结构,每个copy节点的random的指向的位置为前一个节点的random的下一个节点
            copy->random = cur->random->next;
        }
        cur = copy->next;
    }
    //尾插并恢复原链表
    cur = head;
    struct Node* newhead = NULL,* newtail = NULL;
    while(cur)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;
        //这里的尾插是没有哨兵位的尾插,需要判断是否为空
        if(newhead==NULL)
        {
            newhead = newtail = copy;
        }
        else
        {
            newtail->next = copy;
            newtail = newtail->next;
        }
        cur->next = next;
        cur = copy->next;
    }
    return newhead;
}

 

结语:

希望本文章能让您有所收获,您能有所收获就说明我的文章还可以。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

并发-线程的 6 个状态(生命周期)

目录 状态解释 状态间的转化 状态解释 状态间的转化 根据Thread类中定义的枚举类型State值&#xff0c;可以看出有6种状态&#xff1a;可以通过 Thread.getState 方法获得线程的状态NEW&#xff08;新建&#xff09;New&#xff1a;新建了Thread类对象&#xff0c;但是没有启…

软设之进程资源图

进程资源图有两个要素&#xff0c;一个是P&#xff0c;也就是进程&#xff0c;一个是R&#xff0c;可以用R1或者R2等表示&#xff0c;表示资源。 R一般是一个矩形里面有几个圆圈&#xff0c;有几个圆圈就表示有几个资源 这里用R1表示资源&#xff0c;P表示进程 R1P 表示资源…

Tomcat启动闪退怎么解决(文末附终极解决方案)

AI是这么告诉我的 Tomcat启动时出现闪退问题可能由多种原因引起&#xff0c;以下是解决此类问题的一些通用方法&#xff1a; 检查环境变量&#xff1a; 确保已经正确设置了JAVA_HOME和JRE_HOME环境变量&#xff0c;并指向正确的Java安装路径。将Java的bin目录添加到系统的PATH…

频谱模拟器

频谱模拟器&#xff0c;特别是模拟频谱仪&#xff0c;是一种基于特定原理的频谱分析工具。以下是对其的详细介绍&#xff1a; 工作原理&#xff1a; 模拟频谱仪的工作原理主要基于频率转换原理&#xff0c;包括两个关键步骤&#xff1a;信号混频和滤波分析。 信号混频&#xf…

《Fundamentals of Power Electronics》——升压隔离型变换器、SEPIC隔离型变换器

以下是升压型隔离变换器的相关知识点&#xff1a; 升压型隔离变换器可以通过互换降压型隔离变换器的电源与负载的位置得到。升压型隔离变换器有许多种结构&#xff0c;此处简短的讨论两种情况。这些转换器主要使用在高压电源和低谐波整流器中。 图6.36所示是一种全桥型电路结…

【设计模式】13、template 模板模式

文章目录 十三、template 模板模式13.1 ppl13.1.1 目录层级13.1.2 ppl_test.go13.1.3 ppl.go13.1.4 llm_ppl.go13.1.5 ocr_ppl.go 十三、template 模板模式 https://refactoringguru.cn/design-patterns/template-method 如果是一套标准流程, 但有多种实现, 可以用 template …

PR2019软件下载教程

打开下载网址&#xff1a;rjctx.com 选择Premiere&#xff1a; 选择PR2019&#xff0c;并点击&#xff1a; 拉到最后&#xff0c;选择百度网盘下载&#xff1a; 下载到本地。 二&#xff0c;软件安装 解压缩后&#xff0c;双击set_up 选择位置后&#xff0c;进行安装&…

场景文本检测识别学习 day08(无监督的Loss Function、代理任务、特征金字塔)

无监督的Loss Function&#xff08;无监督的目标函数&#xff09; 根据有无标签&#xff0c;可以将模型的学习方法分为&#xff1a;无监督、有监督两种。而自监督是无监督的一种无监督的目标函数可以分为以下几种&#xff1a; 生成式网络的做法&#xff0c;衡量模型的输出和固…

Python爬虫-BeautifulSoup解析

1.简介 BeautifulSoup 是一个用于解析 HTML 和 XML 文档的 Python 库。它提供了一种灵活且方便的方式来导航、搜索和修改树结构或标记文档。这个库非常适合网页抓取和数据提取任务&#xff0c;因为它允许你以非常直观的方式查询和操作文档内容。 2.安装 Beautiful Soup 终端输…

【与 Apollo 共创生态:展望自动驾驶全新未来】

1、引言 历经七年的不懈追求与创新&#xff0c;Apollo开放平台已陆续推出了13个版本&#xff0c;汇聚了来自全球170多个国家与地区的16万名开发者及220多家合作伙伴。随着Apollo开放平台的不断创新与发展&#xff0c;Apollo在2024年4月19日迎来了Apollo开放平台的七周年大会&a…

golang for经典练习 金字塔打印 示例 支持控制台输入要打印的层数

go语言中最经典的for练习程序 金字塔打印 &#xff0c;这也是其他语言中学习循环和条件算法最为经典的联系题。 其核心算法是如何控制内层循环变量j 每行打印的*号数量 j<i*2-1 和空格数量 j1 || j i*2-1 golang中实现实心金字塔 Solid Pyramid和空心金字塔 Hollow Pyram…

ruoyi漏洞总结

若依识别 黑若依 :icon hash"-1231872293 绿若依 :icon hash"706913071” body" 请通过前端地址访 " body" 认证失败&#xff0c;无法访问系统资源 " 如果页面访问显示不正常&#xff0c;可添加默认访问路径尝试是否显示正常 /login?redi…

20232937文兆宇 2023-2024-2 《网络攻防实践》实践八报告

20232937文兆宇 2023-2024-2 《网络攻防实践》实践八报告 1.实践内容 动手实践任务一 对提供的rada恶意代码样本&#xff0c;进行文件类型识别&#xff0c;脱壳与字符串提取&#xff0c;以获得rada恶意代码的编写作者&#xff0c;具体操作如下&#xff1a; &#xff08;1&am…

Deep Learning Part Eight--Attention 24.5.4

01.在翻译、语音识别等将一个时序数据转换为另一个时序数据的任务中&#xff0c;时序数据之间常常存在对应关系 02.Attention 从数据中学习两个时序数据之间的对应关系 03.Attention 使用向量内积&#xff08;方 法之一&#xff09;计算向量之间的相似度&#xff0c;并输出这个…

【C++题解】1658. 游乐设施

问题&#xff1a;1658. 游乐设施 类型&#xff1a;分支结构 题目描述&#xff1a; 游乐场引进了一个新的游乐设施&#xff0c;可以两人一组开动该设施&#xff0c;但设施设计上有一个缺陷&#xff0c;必须一个人的体重在 60 公斤以上&#xff08;包含 60 公斤&#xff09;&am…

CST保存项目时失败?如何解决?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

AI智能名片商城小程序构建企业级私域的IMC模型:IP、MarTech与Content的深度融合

在数字化营销的新时代&#xff0c;为企业定制开发的AI智能名片B2B2C商城小程序&#xff0c;结合我们丰富的私域运营实践&#xff0c;我们深刻领悟到构建企业级私域的三大核心要素&#xff1a;IP&#xff08;企业人设&#xff09;、MarTech&#xff08;营销技术&#xff09;和Co…

飞机起飞降落

第一版&#xff1a;飞机起飞降落脚本 最大速度是1200&#xff0c;螺旋桨速度到1000的时候飞机会上升&#xff0c;到850的时候会下降&#xff0c; 有上升状态&#xff0c;平飞状态和悬浮状态&#xff0c;三个状态按e都可以使螺旋桨减速然后下降 但是是匀速下降&#xff0c;并且…

对命令模式的理解

目录 一、场景1、文本编辑器并不是一个好的例子&#xff0c;设备控制器才是2、设备控制器的demo 二、不用命令模式1、代码2、问题 三、使用命令模式1、代码2、当需求变化时2.1 新增代码2.2 优点 四、进一步思考1、省略对Command的建模可以吗&#xff1f;2、命令模式的价值 一、…

wpf转换器

WPF&#xff08;Windows Presentation Foundation&#xff09;中的转换器主要是指IValueConverter接口的实现&#xff0c;它用于在数据绑定过程中转换源数据和目标数据的类型或表示形式。这种机制使得开发者能够灵活地处理数据&#xff0c;特别是在用户界面&#xff08;UI&…