【数据结构与算法】链表2:节点交换与删除 链表相交 环形链表

news2024/11/15 9:44:27

文章目录

    • 今日任务
    • 1.Leetcode24:两两交换链表中的节点
        • (1)题目
        • (2)思路
        • (3)代码实现
    • 2.Leetcode19:删除链表的倒数第N个节点
        • (1)题目
        • (2)思路
        • (3)快慢指针法
    • 3.Leetcode面试题02.07:链表相交
        • (1)题目
        • (2)思路
        • (3)代码实现
    • 4.Leetcode142:环形链表II
        • (1)题目
        • (2)思路
        • (3)代码实现
    • 5.链表总结

今日任务

  • 24.两两交换链表中的节点

  • 19.删除链表的倒数第N个节点

  • 面试题02.07.链表相交

  • 142.环形链表II

  • 总结

1.Leetcode24:两两交换链表中的节点

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/swap-nodes-in-pairs

(1)题目

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

image-20230218104104240

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

示例 2:

输入:head = []
输出:[]

示例 3:

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

提示:

  • 链表中节点的数目在范围 [0, 100] 内
  • 0 <= Node.val <= 100

(2)思路

前面我们有了链表的相关基础知识,知道了对于链表节点的操作有两种形式:

  • 1.直接使用原来的链表进行删除操作。
  • 2.设置一个虚拟头节点再进行删除操作。

相比较第一种,第二种虚拟头节点的形式更加方便。

初始时,cur指向虚拟头节点,然后依次进行三步:

  • 步骤1:将原链表的头节点变成节点2
  • 步骤2:将原链表的节点2变成一个临时节点tmp(tmp:指向原链表的头节点)
  • 步骤3:将原链表的节点3变成一个临时节点tmp2(tmp2:指向原链表的节点3)(ps:此处这样重复定义是为了后续循环条件的退出)
  • ps:原链表:未加入虚拟头节点的链表,也就是初始化时的链表

image-20230218111454677

操作后的链表:

image-20230218111528059

终止条件:

当cur节点经过第一轮循环时,说明这个链表至少有2个节点,此时cur已经成了原链表的节点2,再进行下一次循环时,如果还有新的节点,只要满足cur节点之后还存在1个或2个节点,循环继续,否则结束循环,并返回原链表的头节点

(3)代码实现

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向head,这样方面后面做删除操作
        ListNode* cur = dummyHead;
        while(cur->next != nullptr && cur->next->next != nullptr) {
            ListNode* tmp = cur->next; // 记录临时节点,原本的头节点
            ListNode* tmp1 = cur->next->next->next; // 记录临时节点,原本的节点3

            cur->next = cur->next->next;    // 步骤一
            cur->next->next = tmp;          // 步骤二
            cur->next->next->next = tmp1;   // 步骤三

            cur = cur->next->next; // cur移动两位,准备下一轮交换
        }
        return dummyHead->next;
    }
};

2.Leetcode19:删除链表的倒数第N个节点

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list

(1)题目

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

image-20230218114024717

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

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

示例 3:

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

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

进阶:你能尝试使用一趟扫描实现吗?

(2)思路

先抓题意,删除倒数第n个节点,我们很自然的就想到快慢指针法,通过设置一个fast指针和一个slow指针,首先让fast指针移动n步,到达目的节点后,fast指针和slow指针再同时移动,直到fast指针移至尾节点,此时slow指针也刚好指向目标节点,那么这里我们只需要让slow->next = slow->next->next即可完成对目标节点的删除。

同样的对于链表的操作,我们还是采取虚拟头节点的方式进行设计。

<1>首先定义fast指针和slow指针,初始值为虚拟头节点:

image-20230218115133608

<2>fast走n+1步(因为加入了虚拟头节点)

image-20230218115254196

<3>fast和slow同时移动,直到fast指向链表末

image-20230218115341005

<4>删除slow指向的下一个节点

image-20230218115452233

(3)快慢指针法

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        while(n-- && fast != NULL) {	// 让fast指向目标节点
            fast = fast->next;
        }
        fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next; 
        
        // ListNode *tmp = slow->next;  C++释放内存的逻辑
        // slow->next = tmp->next;
        // delete nth;
        
        return dummyHead->next;
    }
};

3.Leetcode面试题02.07:链表相交

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci

(1)题目

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

图示两个链表在节点 c1 开始相交:

image-20230218151939703

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

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

示例 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]

进阶:你能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?

(2)思路

根据题意,我们可以有这样一种思路,首先想要找到相交节点,但是可能两个链表的长度不一样,怎么对其是需要考虑的,通过上面的几个示例我们也可以看出,只要让链表1和链表二右对齐即可。

那么在算法中如何实现呢,那么只需要先分别求出两个链表的长度,然后我们就可以得出两个链表长度的差值n,这个差值就是我们对其的关键所在啦。

先让长链表移动n步,然后两个链表同时向后移动,并对节点的数值进行判断是否一致,相同的话就是我们所要求解的相交节点了。

image-20230218161958873

(3)代码实现

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)

4.Leetcode142:环形链表II

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/linked-list-cycle-ii

(1)题目

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

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

不允许修改链表。

示例 1:

image-20230218163000655

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

示例 2:

image-20230218163029607

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

示例 3:

image-20230218163050685

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

提示:

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

进阶:你是否可以使用 O(1) 空间解决此题?

(2)思路

对于这道题的分析,就是为了让我们求解一个链表中是否存在环形链表,如果存在则返回该环形链表的头节点,无环则返回NULL。

对于这道题我们需要解决以下两点:

  • 如何判断链表有环
  • 如果有环,怎么找到这个环的入口

<1>如何判断链表有环

对于环形链表的判断,我们采取快慢指针法,分别定义fast指针和slow指针,从头节点出发,fast指针每次移动2个节点,slow指针移动1个节点,如果fast指针和slow指针在中途相遇,则说明存在环形链表。

由于fast指针走两步,slow指针走一步,那么理论上讲,如果存在环形链表的话是一定存在相遇机会的,动画如下,选自carl大神制作:

141.环形链表

<2>如果有环,怎么找到这个环的入口

既然我们已经有了判断唤醒链表的方式,那么接下来就需要找到环形链表的入口了。

假设从头节点到环形入口的节点数为x,环形入口节点到fast指针与slow指针的相遇节点的节点数为y。

image-20230218173510617

当快指针和慢指针相遇时,快指针的走过的节点数不就等于慢指针走过节点数的两倍嘛,只要我们求出快慢指针走过的节点数,就可以联立成一个等式,并且等式中的x值就是我们要求的结果,那么据此我们可以得出以下结论:

1.slow指针走过的节点数 = x + y

2.fast指针走过的节点数 = x + y + n*(y+z) n:代表slow指针进入环形链表后,此时fast指针在环中的循环次数

3.得到等式:x + y = x + y + n*(y+z) 此处需要注意:n >= 1,因为在环中fast指针必然是会经历一次循环才有可能被slow指针追上,朋友们可以自己推算一遍

4.我们的目标为x,因此化简上式:x = n (y + z) - y

5.当n等于1时,我们可以得知上式结果为:x = z,这就意味着此时从相遇节点到环形链表的入口节点正好等于从头节点到入口节点的长度。

6.根据结论5的分析,我们只需要在fast指针和slow指针相遇时定义一个index指针,同时从头节点也定义一个index2指针,两个指针同时出发,当这两个指针相遇的时候正好就是环形入口的节点

动画如下:

142.环形链表II(求入口)

上面分析的结论是基于n等于1的,那么当循环此处大于1该如何分析呢?

其实即便n大于1,结果也是一样的,不同的是index1指针会在环中多转(n - 1)圈,然后再遇到index2,建议可以做个示例自己试试。

(3)代码实现

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
            // 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
            if (slow == fast) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2; // 返回环的入口
            }
        }
        return NULL;
    }
};

5.链表总结

image-20230218181324408

图片来源: 代码随想录知识星球成员:海螺人

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

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

相关文章

芯片架构RISC-V、X86、ARM三足鼎立

2022 年 7 月&#xff0c;RISC-V 国际基金会首席执行官 Calista Redmond 在嵌入式世界大会上宣布 RISC-V 架构处理器核的出货数量已突破 100 亿颗。 序号架构特点代表性的厂商运营机构发明时间1X86性能高&#xff0c;速度快&#xff0c;兼容性好英特尔&#xff0c;AMD英特尔197…

nginx-服务器banner泄漏风险

http { server_tokens off; # 隐藏Nginx版本号 .... }

桌面美化方案分享+环境配置记录

桌面美化方案分享环境配置记录 旧笔记本是高考结束时候买的&#xff0c;做工稀烂&#xff0c;买来当天键盘就被我敲坏了。当时送去维修&#xff0c;那边的人说暂时没货&#xff0c;要等键盘进货才能换。然后等了四年&#xff0c;键盘还没进到货。 该换了。 旧笔记本上显卡是…

【Vue3源码】第二章 effect功能的完善补充

【Vue3源码】第二章 effect功能的完善补充 前言 上一章节我们实现了effect函数的功能stop和onstop&#xff0c;这次来优化下stop功能。 优化stop功能 之前我们的单元测试中&#xff0c;stop已经可以成功停止了响应式更新&#xff08;清空了收集到的dep依赖&#xff09; st…

nginx+php-fpm整体上线k8s集群之后虚拟内存不断上涨原因排查

背景 为了可以更好的管理我们的lnmp集群&#xff0c;打算将原有的php环境整体打包成一个镜像然后上到k8s容器&#xff0c;这样可以不仅使用到k8s的快速扩缩容和管理的好处&#xff0c;而且让机器资源能更好被利用&#xff0c;减少机器数量 问题 当我们将流量从原有的php机器…

Elasticsearch的安装及常用操作

文章目录一、Elasticsearch的介绍1、Elasticsearch索引2、Elasticsearch的介绍二、Elasticsearch的安装1、安装ES服务2、安装kibana3、Docker安装ES4、Docker安装Kibana三、ES的常用操作1、索引操作2、文档操作3、域的属性3.1 index3.2 type3.3 store总结一、Elasticsearch的介…

关于云计算,我们问了ChatGPT 10个问题

ChatGPT懂云计算吗&#xff1f;前些天&#xff0c;我们问了ChatGPT&#xff08;非Plus收费版&#xff09;一些问题。1. 什么是云计算&#xff1f;2. 云计算行业的护城河是什么&#xff1f;3. 什么是云原生&#xff1f;4. 微软Azure与亚马逊AWS的主要区别是什么&#xff1f;5. 为…

你真的会做APP UI自动化测试吗?我敢打赌百分之九十的人都不知道这个思路

目录 前言 一&#xff0c;开发语言选择 二&#xff0c;UI测试框架选择 1&#xff0c;Appium 2&#xff0c;Airtest 3&#xff0c;选择框架 三&#xff0c;单元测试框架选择 四&#xff0c;测试环境搭建 1&#xff0c;测试电脑选择 2&#xff0c;测试手机选择 3&#…

6.关于系统服务的思考—— native vs java

文章目录native服务 以sensor service为例Native 服务java 服务&#xff0c; 以vibrate为例java 服务 以一个demo为例native服务 以sensor service为例 service启动 SystemServer.startBootstrapServices---->>>mSystemServiceManager.startService—>>>Sen…

SQL语句创建视图:

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言初阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍数据库中有关视图的知识,参考学校作业. 金句分享:…

基于SpringBoot的在线文档管理系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…

进程-操作系统结构

进程-操作系统结构 中文仅本人理解&#xff0c;有错误请联系我。 操作系统为不同方面服务&#xff0c;有不同的设计角度。 为用户&#xff1a; 使用 为程序员&#xff1a;创造 程序员需要关注的就是system call接口的调度 file systems&#xff1a;ntfs&#xff0c;ext4 commu…

eclipse快捷开发学习笔记

快速收起java类中的所有代码当类中方法过多时&#xff0c;收起所有方法&#xff0c;可以方便查看注释找到方法右击左侧栏任意行号位置-点击Folding-点击Collapse All效果图如下代码格式化混乱的代码格式化后&#xff0c;方便阅读分析菜单栏-Source-Format效果如下3.查看方法被哪…

Bland-Altman图

介绍 Bland-Altman图是一种一致性评价测量方法&#xff0c;简称BA&#xff0c;常用于医学实验和数据分析。 可使用它检测两组数据的一致性&#xff0c;比如对比新旧两种方法&#xff0c;对比一组实际值和预测值等。相对于校准曲线&#xff0c;它能更好地对比两组数据中每个数据…

Unit Test and Integration Test

Unit Test and Integration Test Background It is the first time that I try to write an article in English. In the past, I didn’t write test code. Just thinking QA is responsible for testing. As a developer, I don’t need to care about tests. Although I …

Robust Self-Augmentation for Named Entity Recognition with Meta Reweighting

摘要 近年来&#xff0c;自我增强成为在低资源场景下提升命名实体识别性能的研究热点。Token substitution and mixup &#xff08;token替换和表征混合&#xff09;是两种有效提升NER性能的自增强方法。明显&#xff0c;自增强方法得到的增强数据可能由潜在的噪声。先前的研究…

【Vue3源码】第四章 实现isReadonly和isReactive

【Vue3源码】第四章 实现isReadonly和isReactive 前言 上一章节我们实现readonly API&#xff0c;并且优化之前写的reactive API。这一章我们实现isReadonly和isReactive两个API。 1、实现isReactive 官网是这么介绍的&#xff1a;检查一个对象是否是由 reactive() 或 shallo…

WebView2 (Chromium) 右键菜单大全

在开发WebView2浏览器时&#xff0c;需要定制右键上下文菜单&#xff0c;但是微软的官方网站上没有详细的菜单&#xff0c;这里整理出来&#xff0c;以方便大家使用。以下是在win11上测试获取的&#xff0c;经过测试win7上稍有不同。一、网页上空白处右键 Target&#xff1a;Pa…

Mybatis Notes

文章目录1 Mybatis 介绍1.1 快速入门2 JDBC2.1 JDBC介绍2.3 JDBC问题分析2.4 Mybatis与JDBC技术对比3 数据库连接池3.1 数据库连接池介绍3.2 数据库连接池产品产品3.3 Druid引入项目4lombok4.1 lombok介绍4.2 lombok使用4.2.1 在pom.xml文件中引入依赖4.2.2 pojo类代码引入1 My…

强化学习 Reinforcement Learning(1) ~ 介绍

1. 强化学习概念和分类 强化学习&#xff0c;Reinforcement Learning 通过价值选行为&#xff1a; Q LearningSarsaDeep Q Network 直接选行为&#xff1a; Policy Gradients 想象环境并从众学习&#xff1a; Model Based RL 1.1 通过环境分类 1.1.1 不理解环境 Model-Fr…