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

news2024/12/26 13:26:00

文章目录

    • 今日任务
    • 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/354600.html

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

相关文章

机试_6_数据结构(二)

本文介绍机试中考查的一些非线性数据结构&#xff0c;包括二叉树、二叉排序树、优先队列和散列表等较为高级的数据结构。 一、二叉树 树的结构有诸多变体&#xff0c;它们在各种应用中发挥着重要作用。 作为树的特例的二叉树(Binary Tree)&#xff0c;虽然看似简单&#xff0…

Ubuntu安装boost库

参考链接&#xff1a;https://blog.csdn.net/zeye5731/article/details/122413193 1、下载 boost库 boost 库各大版本下载&#xff1a;boost download | hisroy versions 下面就以安装 1.78.0的版本为例 2、安装boost库 我们将下载好的boost库上传到Ubuntu&#xff0c;并解…

C++设计模式(16)——责任链模式

亦称&#xff1a; 职责链模式、命令链、CoR、Chain of Command、Chain of Responsibility 意图 责任链模式是一种行为设计模式&#xff0c; 允许你将请求沿着处理者链进行发送。 收到请求后&#xff0c; 每个处理者均可对请求进行处理&#xff0c; 或将其传递给链上的下个处理…

关闭终端后在服务器上运行代码+将终端输出打印到文件中

解决方案 首先打开一个screen # name是你想给你的项目在screen中起的名字 screen -S name 然后&#xff0c;在你的运行命令后加入 | tee xxxx.txt&#xff0c;如 python run_mujoco.py --envWalker2d-v2 --tradeoff0.2 | tee Walker2d-v2.txt 这样就可以实现题目中的目的了…

华测导航GPCHC协议ROS驱动包,CGI610、410接收机,NavSatStatus、GPSFix和普通格式

目录一、消息类型1.1 sensor_msgs/NavSatFix1.2 sensor_msgs/NavSatStatus1.3 gps_common::GPSFix1.4 sensor_msgs::Imu二、部分源码2.1 相关的依赖和库2.2 文件结构2.3 字段分割函数2.4 定义消息话题Ubuntu 20.04 noetic 华测CGI 610——RS232-C——GPCHC 一、消息类型 1.1 …

从零编写linux0.11 - 第十一章 可执行文件

从零编写linux0.11 - 第十一章 可执行文件 编程环境&#xff1a;Ubuntu 20.04、gcc-9.4.0 代码仓库&#xff1a;https://gitee.com/AprilSloan/linux0.11-project linux0.11源码下载&#xff08;不能直接编译&#xff0c;需进行修改&#xff09; 本章目标 本章会加载并运行…

SpringCloud(PS)远程调用--Feign

远程调用RestTemplate远程调用RestTemplate方式调用存在的问题Http客户端Feign实现步骤自定义配置Feign优化Feign性能优化——连接池配置最佳实践RestTemplate远程调用 Bean // LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}Autowiredprivat…

linux基本功系列之fdisk命令实战

文章目录前言一. fdisk命令介绍二. 语法格式及常用选项三. 参考案例3.1 列出每个分区的大小3.2 分区操作3.2.1 添加硬盘3.2.2 开启虚拟机并分区3.3.3 分区完成后进行格式化挂载四 . 设置分区自动挂载前言 大家好&#xff0c;又见面了&#xff0c;我是沐风晓月&#xff0c;本文…

Elasticsearch7学习笔记(尚硅谷)

文章目录一、ElasticSearch概述1、ElasticSearch是什么2、全文搜索引擎3、ElasticSearch 和 Solr3.1 概述3.2 比较总结二、Elasticsearch入门1、Elasticsearch安装1.1 下载使用1.2 数据格式2、索引操作3、文档操作&#xff08;了解&#xff09;3.1 创建文档3.2 文档查询3.3 文档…

外贸谷歌优化,外贸google SEO优化费用是多少?

本文主要分享关于做外贸网站的谷歌seo成本到底需要投入多少这一件事。 本文由光算创作&#xff0c;有可能会被剽窃和修改&#xff0c;我们佛系对待这种行为吧。 那么外贸google SEO优化费用是多少&#xff1f; 答案是&#xff1a;2w~25w。 好&#xff0c;看到这个答案先别激…

0.1opencv库VS环境配置

opencv环境配置 感谢大家学习这门教程。本系列文章首发于公众号【周旋机器视觉】。 这个这门课程的第一篇文章&#xff0c;主要是opencv环境配置。 本教程的环境为 Visual Studio 2019CMake 3.22.3opencv 4.6.0windows 10 1、opencv的源码下载与安装 直接访问opencv官网&…

Docker入门教程

文章目录一、Docker概述1. 什么是容器技术&#xff1f;2. 什么是Docker3. 为什么要使用Docker4. Docker和虚拟机的对比5. Docker相关概念6. DockerHub7. Docker架构二、安装Docker1. 安装Docker2. 配置阿里云镜像加速三、Docker常用命令1. 帮助命令2. 镜像操作命令3. 容器操作命…

mysql 8.0 忘记root密码-linux

vim /etc/my.cnf 在[mysqld]最后加上如下语句&#xff1a;skip-grant-tables 并保存退出有的配置是分开的&#xff0c;/etc/my.cnf.d/mysql-server.cnf重启mysql服务 : service mysqld restart免密码登陆: mysql -u root -ppassword校验直接回车select host, user, authenticat…

C#:Krypton控件使用方法详解(第七讲) ——kryptonHeader

今天介绍的Krypton控件中的kryptonHeader&#xff0c;下面开始介绍这个控件的属性&#xff1a;控件的样子如上图所示&#xff0c;从上面控件外观来看&#xff0c;这个控件有三部分组成。第一部分是前面的图片&#xff0c;第二部分是kryptonHeader1文本&#xff0c;第三部分是控…

前端学习第一阶段-第7章 品优购电商项目

7-1 品优购项目介绍及准备工作 01-品优购项目导读 02-网站制作流程 03-品优购项目规划 04-品优购项目搭建 05-品优购项目-样式的模块化开发 06-品优购项目-favicon图标制作 07-品优购项目-TDK三大标签SEO优化 7-2 首页Header区域实现 08-品优购首页-快捷导航shortcut结构搭建 0…

MySQL(三):切分,主从复制,读写分离

文章目录一、切分水平切分垂直切分水平切分策略二、主从复制三、读写分离一、切分 水平切分 水平切分又称为sharding,它是将同一个表中的记录拆分到多个结构相同的表中。当一个表的数据不断增多的时候&#xff0c;sharding是必然的选择&#xff0c;它可以将数据分布到集群的不…

jmap监控工具

在一个JVM进程中会存在有多个对象实例,如果要想获取所有对象的信息,就可以通过JDK提供的jmap工具完成,另外使用该工具还可以直接获取到指定进程的堆内存使用信息,开发者可以通过jmap --help 命令查看该命令相关的参数。 1、查看JVM进程中的对象信息 导致JVM性能问题的核心…

力扣sql简单篇练习(十九)

力扣sql简单篇练习(十九) 1 查询结果的质量和占比 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # 用count是不会统计为null的数据的 SELECT query_name,ROUND(AVG(rating/position),2) quality,ROUND(count(IF(rating<3,rating,null))/count(r…

九龙证券|欧美充电桩市场快速增长 国内“桩企”出海需求旺盛

海外充电桩毛利率水平高、需求增加速&#xff0c;“桩企”出海继续。 在阿里世界站上接连三年火爆的新能源出口行业&#xff0c;本年坚持高增加下&#xff0c;涌现出新商机——新能源车充电桩。阿里世界站最新跨境指数显现&#xff0c;曩昔一年新能源车充电桩的海外商机快速增加…

SpringBoot11:分布式Dubbo、Zookeeper

什么是分布式系统&#xff1f; 建立在网络之上的软件系统&#xff0c;是若干个独立的计算机的集合&#xff0c;但是对用户来说&#xff0c;就像单个系统一样。可以利用更多的机器&#xff0c;处理更多的数据 注意&#xff1a;只有当单个节点不够用的时候&#xff0c;才需要考…