算法刷题Day4 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II

news2024/11/19 19:22:28

目录

  • 0 引言
  • 1 两两交换链表中的节点
    • 1.1 我的解题
    • 1.2 注意事项
  • 2 删除链表的倒数第N个节点
    • 2.1 我的代码
    • 2.2 报错原因分析
  • 3 链表相交
    • 3.1 我的解题
  • 4 环形链表II
    • 4.1 我的解题

请添加图片描述

  • 🙋‍♂️ 作者:海码007
  • 📜 专栏:算法专栏
  • 💥 标题:算法刷题Day4 | 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、面试题 02.07. 链表相交、142.环形链表II
  • ❣️ 寄语:书到用时方恨少,事非经过不知难!

0 引言

1 两两交换链表中的节点

  • 🎈 文档讲解:https://programmercarl.com/0024.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.html
  • 🎈 视频讲解:
  • 🎈 做题状态:没看题解就写出来了,比较顺利

1.1 我的解题

这一次还是会涉及到头结点和非头结点处理的差别,所以引入了虚拟头结点统一操作。
这一题涉及到三个指针,cur、pre、tmp。一定要注意cur是从虚拟头结点开始遍历。然后当 cur->next; cur->next->next; cur的后面两个节点都存在时才能进行遍历。可以先看图解的思路

在这里插入图片描述

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        // 创建虚拟头结点
        ListNode* dummyNode = new ListNode(-1);
        dummyNode->next = head;

        // 创建三个节点指针,主要为了完成交换操作
        ListNode* cur = dummyNode;
        ListNode* pre;
        ListNode* tmp;

        // 当cur指针、cur的下一个节点指针、以及下下一个指针存在时,才能进行交换
        // 因为cur第一次指向的是虚拟头结点,后面两个指针才是真正的数据
        while ( cur && cur->next && cur->next->next)
        {
            pre = cur->next;
            tmp = pre->next;    // tmp可以为空指针,不影响
            // 交换节点
            cur->next = tmp;
            pre->next = tmp->next;
            tmp->next = pre;

            // 交换完成后,cur指针向后移动两位,因为是两两交换
            cur = cur->next->next;
        }

        return dummyNode->next;
    }
};

1.2 注意事项

一定要画图,不画图,操作多个指针很容易乱,而且要操作的先后顺序。

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

  • 🎈 文档讲解:
  • 🎈 视频讲解:
  • 🎈 做题状态:通过画图,然后写下步骤,做题变轻松很多

2.1 我的代码

具体思路如下
在这里插入图片描述

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 1. 创建虚拟头结点
        ListNode* dummyNode = new ListNode(0);
        dummyNode->next = head;

        // 2. 循环n次,将right指针移动到正确位置
        ListNode* left = dummyNode;
        ListNode* right = dummyNode;
        for (int i = 0; i < n; i++)
        {
            right = right->next;
            // 如果倒数第n个节点不存在,则直接返回
            if (right == nullptr) return dummyNode->next;
        }

        // 3. 遍历到最后的空节点
        while (right->next != nullptr)
        {
            right = right->next;
            left = left->next;
        }

        std::cout << "right->val = " << right->val;
        // 4. 删除节点
        ListNode* tmp = left->next;
        left->next = tmp->next;
        delete tmp;
        return dummyNode->next;
    }
};

2.2 报错原因分析

但是我在删除目标节点的时候报错了,因为我应该将 left->next = tmp->next; 而不是指向right 节点。

在这里插入图片描述

3 链表相交

  • 🎈 文档讲解:https://programmercarl.com/%E9%9D%A2%E8%AF%95%E9%A2%9802.07.%E9%93%BE%E8%A1%A8%E7%9B%B8%E4%BA%A4.html
  • 🎈 视频讲解:https://programmercarl.com/%E9%9D%A2%E8%AF%95%E9%A2%9802.07.%E9%93%BE%E8%A1%A8%E7%9B%B8%E4%BA%A4.html
  • 🎈 做题状态:没太大问题

3.1 我的解题

需要注意的就是,判断的是节点的地址是否相等,而不是节点内存储的val是否相等

在这里插入图片描述

class Solution {
public:
    ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
        // 创建虚拟头结点(和前面思路保持一致,习惯使用虚拟头结点)
        ListNode* dummyA = new ListNode(0);
        dummyA->next = headA;
        ListNode* dummyB = new ListNode(0);
        dummyB->next = headB;

        // 1.确定最小的长度值
        int sizeA = 0;
        int sizeB = 0;

        ListNode* curA = dummyA->next;
        while (curA != nullptr)
        {
            sizeA++;
            curA = curA->next;
        }

        ListNode* curB = dummyB->next;
        while (curB != nullptr)
        {
            sizeB++;
            curB = curB->next;
        }


        // 2. 将curA 和 curB 指针对齐
        // 注意此时需要重新给 curA 和 curB 重置到起始位置
        curA = dummyA->next;
        curB = dummyB->next;
        if (sizeA > sizeB)
        {
            // 当链表A较长时,需要移动 curA 指针和 curB对齐
            int dValue = sizeA - sizeB;
            while (dValue > 0)
            {
                curA = curA->next;
                dValue--;
            }
        }
        else if (sizeA < sizeB)
        {
            // 当链表B较长时,需要移动 curB 指针和 curA 对齐
            int dValue = sizeB - sizeA;
            while (dValue > 0)
            {
                curB = curB->next;
                dValue--;
            }
        }

        // 3. 开始遍历 curA 和 curB ,看什么时候元素值相等
        while (curA != nullptr && curA != curB)
        {
            curA = curA->next;
            curB = curB->next;
        }

        return curA;
    }
};

4 环形链表II

  • 🎈 文档讲解:https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html
  • 🎈 视频讲解:https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html
  • 🎈 做题状态:有难度

4.1 我的解题

不看题解写不出来,虽然已经想到了快慢指针确定是否有环,但是不知道该如何确定环的入口位置。

有两个关键点:

  • 快慢指针为什么一定会在环内相遇

    • 因为判断是否相遇时,不仅移动完快指针后会判断,移动慢指针后也会判断。所以就算快指针跳过慢指针,慢指针移动上去后还是会相遇。
    • 下图是代码随想录中的图片,图中的快指针就是刚好跳过慢指针,但是慢指针移动后就相遇了。在这里插入图片描述
  • 如何确定环的入口。

    • 根据快慢指针的关系,可以推到出 x = (n - 1) (y + z) + z 公式。
    • 然后在快慢指针相遇的时候,再新建一个指针 cur 从头结点位置出发,然后 cur 指针一定会和慢指针在环的入口处相遇。
      在这里插入图片描述

解题步骤(本题没有头结点特殊情况)

  1. 定义快慢指针,并初始化到头结点位置。
  2. 移动快指针移动两次,移动慢指针一次,然后判断是否相等,如果相等则找到相遇点。
  3. 找到相遇点后,再新建一个指针 cur ,然后循环遍历知道 cur 指针和 slow 指针相遇,相遇的节点就是环的入口节点。
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        ListNode* cur = head;

        // 循环条件是 fast 不为空指针
        while ( fast!= nullptr )
        {
            fast = fast->next;
            // 由于 fast 要移动两次,所以每移动一次都需要判断是否为空
            if ( fast != nullptr )
            {
                fast = fast->next;
                slow = slow->next;
                if (fast == slow) break;
            }
            else
            {
                return nullptr;
            }
        }

        // 如果 fast 为空说明链表没有环,则返回空
        if (fast == nullptr) return nullptr;

        cout << "环的入口节点的值 = " << slow->val;

        // 新建一个指针从head开始遍历
        
        while (cur != slow)
        {
            cur = cur->next;
            slow = slow->next;
        }

        // cur 和 slow 相遇后退出循环,此时cur的位置就是环的入口节点
        return cur;
    }
};

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

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

相关文章

10个让你事半功倍的工作效率技巧,让你成为高效率的现代人!

作为现代人&#xff0c;我们每天都在面对各种各样的任务和工作&#xff0c;有时候会因为繁忙而感到力不从心&#xff0c;导致效率变得非常低下。所以&#xff0c;在这篇文章中&#xff0c;我将分享10个能够帮助你提高工作效率的技巧&#xff0c;让你的工作事半功倍。 1. 制定计…

labview中6种机械动作的区别

1.单击时转换&#xff1a;单击时转换&#xff0c;需要手动转换或者赋值回复原来状态&#xff1b; 2.释放时转换&#xff1a;释放时时转换&#xff0c;需要手动转换或者赋值回复原来状态&#xff1b; 3.单击时转换保持到鼠标释放&#xff1a;触发两次&#xff0c;自动恢复原来状…

ppocr ERROR: When parsing line KeyError: None

训练百度飞桨 paddleOCR模型。 数据集没有问题&#xff0c;但报以上错误。 发现是手欠&#xff0c;多按了下回车&#xff0c;多了个 - 删掉就好了。

map和set的介绍和使用

文章目录 map和set关联式容器键值对set介绍模板参数 map介绍模板参数为什么map支持下标访问 multiset介绍 multimap map和set 关联式容器 我们在之前讲过STL的一些基础容器&#xff0c;例如vector&#xff0c;list&#xff0c;deque&#xff0c;forward_list等 这些其实统一…

Script标签中 defer 和 async 属性的区别

script 标签中 defer & async 属性 如果我们注意过 html 中的 <script> 标签&#xff0c;就会发现在有的加载 JavaScript 文件的 <script src"https://code.jquery.com/jquery-3.7.1.min.js" defer></script>这里就探讨一下 script 标签上的…

[AutoSar]BSW_Com013 CAN TP 模块配置

目录 关键词平台说明一、缩写对照表二、Functional Description&#xff08;vector&#xff09;2.1 Asynchronous and Synchronous behavior of CanTp_Transmit2.1.1 asynchronous 2.1.2 synchronous2.2 Separation Time by Application 三、CanTpChannels3.1 接收端3.2 发送端…

【Web】浅聊Java反序列化之C3P0——URLClassLoader利用

目录 前言 C3P0介绍 回归本源——序列化的条件 利用链 利用链分析 入口——PoolBackedDataSourceBase#readObject 拨云见日——PoolBackedDataSourceBase#writeObject 综合分析 EXP 前言 这条链最让我眼前一亮的就是对Serializable接口的有无进行了一个玩&#xff0c…

day42 动态规划part4

先遍历物品还是先遍历背包二刷再考虑吧。累了&#xff0c;不想停留太久。 背包问题 二维 &#xff08;卡码网题目&#xff09; 各种解释&#xff1a; 要理解的是这个表格每一个格子都是当前所处情况的最大价值&#xff0c;我们用已经推导出的最大价值来推导当前情况的最大价值…

2.案例、鼠标时间类型、事件对象参数

案例 注册事件 <!-- //disabled默认情况用户不能点击 --><input type"button" value"我已阅读用户协议(5)" disabled><script>// 分析&#xff1a;// 1.修改标签中的文字内容// 2.定时器// 3.修改标签的disabled属性// 4.清除定时器// …

GUROBI之数学启发式算法Matheuristics

参考运小筹的帖子&#xff1a;优化求解器 | Gurobi 数学启发式算法&#xff1a;参数类型与案例实现 - 知乎 (zhihu.com) 简言之&#xff0c;数学启发式是算法就是数学规划和启发式算法的融合&#xff0c;与元启发式算法相比&#xff0c;数学启发式算法具有更强的理论性。 在GUR…

WEB区块链开发组件 - KLineChart

当我们开发区块链的时候&#xff0c;实现K线可能大家会想到EChart&#xff0c;但是EChart做可能需要耗费大量工作量&#xff0c;实现出来的功能估计也是牵强着用。 这时候&#xff0c;我们可能网上会搜索到TradingView,可是这个组件虽然功能非常强大&#xff0c;但是还是要费事…

视觉图像处理和FPGA实现第三次作业--实现一个加法器模块

一、adder模块 module adder(ina, inb, outa); input [5:0] ina ; input [5:0] inb ; output [6:0] outa ;assign outa ina inb; endmodule二、add模块 module add(a,b,c,d,e); input [5:0] a ; input [5:0] b ; input [5:…

Matlab R2021a安装教程(附带免费安装包)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 Matlab简介 Matlab是一种高级技术计算语言和交互式环境&#xff0c;用于算法开发、数据可视化和数值计算。它集成了数学、工程和科…

unity中实现场景跳转

1&#xff0c;第一步创建2个场景&#xff08;右键资源窗口&#xff0c;名字这里我取的1111和2222&#xff09; 2.添加跳转按钮&#xff08;双击其中一个场景并添加按钮&#xff09; 3.编辑按钮的文字&#xff08;将原本的按钮打开点击里面的text&#xff0c;就可以在右边编辑文…

MySQL 多种日期处理函数介绍

MySQL 提供了多种日期处理函数&#xff0c;用于处理和操作日期和时间数据。这些函数可以帮助你执行如日期计算、时间转换、格式化输出等操作。以下是一些常用的 MySQL 日期处理函数及其用法&#xff1a; 日期和时间格式化函数 1. **DATE_FORMAT()**&#xff1a;将日期或时间戳格…

AI壁纸号一周增加上千粉丝,轻松变现的成功案例分享

前言 随着AI绘画技术的发展&#xff0c;传统的互联网副业壁纸号在新的技术加持下迎来了第二春。本文将分享一位壁纸号创作者的成功案例&#xff0c;并为大家提供创作门槛和硬件要求等相关信息。 该项目的创作门槛极低&#xff0c;基本上可以由AI完成内容创作。不过&#xff0…

使用python实现一个dicom影像解析入库程序demo

简介 DICOM&#xff08;Digital Imaging and Communications in Medicine&#xff09;是医学图像和相关信息的国际标准。它定义了医学影像的格式和通信协议&#xff0c;使得不同设备和系统之间可以交换和共享医学图像和相关数据&#xff0c;如CT扫描、MRI图像、超声波图像等。…

代码随想录算法训练营第七天| 454.四数相加II、383.赎金信、15.三数之和、18.四数之和

系列文章目录 目录 系列文章目录454.四数相加II使用HashMap法 383.赎金信哈希解法&#xff08;数组&#xff09; 15.三数之和双指针法 18.四数之和双指针法 454.四数相加II 题解&#xff1a;该题和1.两数之和的方法是一样的&#xff0c;这个题的难点在于key和value分别是什么。…

发那科数控机床FanucCNC(NCGuide)仿真模拟器配置和数据采集测试

开发日记3.12 此篇用于记录发那科数控机床(Fanuc CNC)采集程序开发中&#xff0c;用虚拟机做测试时&#xff0c;虚拟机的配置和使用以支持采集软件开发和测试。 配置虚拟机使用仿真软件 下载VMware15 「链接&#xff1a;https://pan.xunlei.com/s/VNsl9Gmb14ANBiiNlsT7vA2LA…

Day15 面向对象进阶——接Day14

Day15 面向对象进阶——接Day14 文章目录 Day15 面向对象进阶——接Day14一、访问修饰符二、Object三、深入String的equals()方法四、final 一、访问修饰符 1、含义&#xff1a;修饰类、方法、属性&#xff0c;定义使用的范围 2、经验&#xff1a; 2.1.属性一般使用private修…