算法: 双指针

news2024/9/22 15:43:36

题目:环形链表

题目讲解:

判断环

要判断链表是否有环,可以使用快慢指针的方法。快指针每次走两步,慢指针每次走一步。如果链表有环,快慢指针最终会相遇;如果没有环,快指针会先到达链表末尾。

为什么快指针走两步,慢指针走一步?因为这样快指针会更快进入环,并在环中追上慢指针。具体来说,当快指针进入环后,它将在环中不断接近慢指针,最终相遇。即使快指针在环中比慢指针快,它们也不会永远错过,而是必然会在某一时刻相遇。

这个相遇的原因是因为在每一次循环中,快指针都比慢指针多走一步,这样慢指针总会缩小它与快指针之间的距离,直至两者相遇。

如果操场是一个环形的(即链表有环),那么即使快跑者一开始远远超过慢跑者,由于他们在同一条跑道上不断绕圈,快跑者最终一定会追上慢跑者。这是因为在每一圈中,快跑者都会比慢跑者多跑一圈的距离,最终两人会在某个点相遇。

代码实现:

class Solution 
{
public:
    bool hasCycle(ListNode *head) 
    {
        //判断空链表及单节点
       if(head == nullptr || head->next == nullptr)
       {
        return false;
       }
        ListNode* fast = head->next;
        ListNode* slow = head;
        while(fast != nullptr && fast->next != nullptr)
        {
        fast = fast->next->next;
        slow = slow->next;
         if(fast == slow)
           {
            return true;
           } 
        }
        return false;
    }
};

注意事项:

在处理链表时,必须仔细考虑 while 循环的判断条件。如果链表不是环形的,快指针每次移动两步可能会最终指向空引用,导致野指针问题。为了避免这种情况,你需要提前检查快指针的下一个节点是否为空。使用 && 运算符可以确保两个条件都为真,这样可以防止当链表只有两个节点时发生错误——在这种情况下执行 fast = fast->next->next; 会导致野指针。

关于快指针最初指向第一个节点而慢指针指向空的做法,在这里并不适用。如果 slow 为空,执行 slow = slow->next; 会导致空指针引用错误。因此,必须根据具体问题定制你的方法,而不是盲目套用模式。

题目:环形链表||

题目讲解:

找环入口

数学解释

假设链表中从头到环入口点的距离是 a

环的长度是 b + c(其中 b 是从环入口点到 slowfast 相遇点的距离

c 是从相遇点再回到入口点的距离。

相遇时slow走过的路程是 a + b

相遇时fast走过的路程是 a + (b+ c) + b

又因为fast走的路程是slow的两倍所以 2(a + b) ==  a + (b+ c) + b ->  a = c

所以头到入口的距离就等于相遇点到入口的距离,这样的话让它们从头和快慢指针相遇的地点一起出发,等什么时候它们相遇那就什么时候遇到入口。

快慢指针相遇的理解

fast 指针比 slow 指针更早进入环,并且在环内移动得更快。当 slow 进入环时,fast 就开始追赶它。由于 fast 每次走两步,而 slow 只走一步,这就好像 fast 在不断地逼近一个“静止”的目标。你可以把这情况类比为 slow 不动,而 fast 每次向前移动一步。在这种情况下,fast 会很快追上 slow

因此,slow 并不需要走完整个环就会被 fast 追上。相遇是必然的,而 slow 没走完一整圈就和 fast 相遇的情况,正是因为 fast 在追击 slow 的过程中缩短了它们之间的距离。

代码实现:

class Solution 
{
public:
    ListNode *detectCycle(ListNode *head) 
    {
        if(head == nullptr || head->next == nullptr)
        {
            return nullptr;
        }
        ListNode* fast = head;
        ListNode* slow = head;
        ListNode* Ptr = head;
        while(fast != nullptr && fast->next != nullptr)
        {
           fast = fast->next->next;
           slow = slow->next;
           if(fast == slow)
           {
             while(Ptr != fast)
             {
                Ptr = Ptr->next;
                fast = fast->next;
             }
             return Ptr;
           }
        }
        return nullptr;
    }
};

题目:寻找重复数

题目讲解:

数组该怎样链表化?

你是否想过,数组中的每个元素其实可以看作链表中的一个节点?那么,这些节点该如何指向彼此呢?链表中的指向并不是随意的,它靠什么来决定呢?

答案就在数组的下标和元素之间的关系。下标永远是固定的,从 0 开始不变,而真正“变动”的是数组中的元素。每个元素的值决定了它指向的下一个位置。比如,数组的第一个元素是 1,它就会指向下标为 1 的位置。如果下标 1 的元素是 3,那么它再指向下标 3 的位置。如此循环下去,直到某个节点被指向两次,这就意味着找到了重复的数字。这样,你就把数组成功“链表化”了,利用这个方法轻松找出数组中的重复元素。

代码实现:

class Solution 
{
public:
    int findDuplicate(vector<int>& nums) 
    {
      int fast = 0;
      int slow = 0;
      
      do
      {
        slow = nums[slow];
        fast = nums[nums[fast]]; // 走两步
      }while(slow != fast);

      int tmp = 0;
      while(tmp != fast)
      {
       tmp = nums[tmp];
       fast = nums[fast];
      }
      return tmp;
    }
};

题目:快乐水

题目讲解:

这个题目其实和之前的三道题思路类似。题目给出了两种情况:第一种是计算结果为 1,然后进入无限循环,循环的数字全是 1;第二种是永远算不出 1,进入一个与 1 无关的数字循环。这个循环可以被看作一个“环”,要么全是 1,要么是其他数。我们需要找到这个“环”的入口,也就是第一个 1 或者与 1 无关的数。

其实,这就是在找环的入口,完全可以用快慢指针来解决。快慢指针的核心思想是,一个走得快,一个走得慢,只要能满足这个条件,就可以称为快慢指针。在这里,我们让慢指针每次变化一次,快指针每次变化两次。如果它们相遇且值为 1,就返回 true,否则返回 false

代码实现:

class Solution 
{
public:
    int qwe(int number)
    {
        int sum = 0;
        int tmp = 0;
        while(number)
        {
          tmp = number % 10;
          sum += tmp * tmp;
          number = number / 10;
        }
        return sum;
    }
    bool isHappy(int n) 
    {
       int slow = n;
       int fast = n;
       do
       {
        slow = qwe(slow);
        fast = qwe(fast);
        fast = qwe(fast);
       }
       while(slow != fast);
      if(slow == 1)
      {
        return true;
      }
      return false;
    }
};

题目:删除链表的倒数第N个元素

题目讲解:

这道题可以先让快指针走n步然后再让慢指针和快指针一同走那这样快指针走到结尾那慢指针也就刚好移动到目标位置的前一个。

代码实现:

class Solution 
{
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) 
    {
     ListNode* dummyHead = new ListNode(0 , head);
     dummyHead->next = head;
     ListNode* fast = dummyHead;
     ListNode* slow = dummyHead;
      for (int i = 0; i <= n; ++i) 
    {
       fast = fast->next;
    }
     while(fast)
     {
        fast = fast->next;
        slow = slow->next;
     }
     slow->next = slow->next->next;
     return dummyHead->next;
    }
};

这里定义哨兵位(虚拟头节点)的作用:
 

为什么要用虚拟头节点(哨兵位)?

  • 当我们需要删除链表中的某个节点时,通常需要访问待删除节点的前一个节点,因为删除操作涉及到将前一个节点的 next 指针重新指向待删除节点的下一个节点。

2. 删除头节点的特殊情况

  • 如果没有虚拟头节点,要删除头节点会比较麻烦,因为头节点前面没有其他节点,无法轻易访问它的前一个节点。因此,删除头节点通常需要特别处理,直接返回第二个节点来实现删除。

3. 虚拟头节点解决了什么问题?

  • 引入虚拟头节点后,即使是删除头节点的操作,我们也可以按照统一的方式处理。虚拟头节点的 next 指向实际的头节点,这样我们就能在删除任何节点时,不需要再为删除头节点编写额外的特殊逻辑了。

4. 总结

  • 统一的删除逻辑:有了虚拟头节点后,删除头节点就变得和删除其他节点一样,不需要再单独处理特殊情况。
  • 代码简洁性:使用虚拟头节点让代码更简洁,因为所有节点的删除操作都可以通过相同的逻辑来处理,无需特别关注链表的第一个节点。

题目:链表的中间节点

题目讲解:

这题比较easy,所以我就贴代码算了。

代码实现:

class Solution 
{
public:
    ListNode* middleNode(ListNode* head) 
    {
     ListNode* fast = head;
     ListNode* slow = head;
     while(fast && fast->next)
     {
        fast = fast->next->next;
        slow = slow->next;
     }
     return slow;
    }
};

题目:回文链表

题目讲解:

判断回文链表我们可以先利用快慢指针找到前部分的尾部然后然后再去反转后部分链表判断,然后再去判断它们的val是否一摸一样,我们唯一要注意的就是反转链表,

代码实现:

class Solution 
{
public:
//反转中间节点后的数据
ListNode* reverse(ListNode* Newhead)
{
ListNode* PPfast = Newhead;
ListNode* PPslow = nullptr;
 while(PPfast)
 {
    ListNode* next = PPfast->next;
    PPfast->next = PPslow;
    PPslow = PPfast;
    PPfast = next;
 }
 Newhead = PPslow;
 return Newhead;
}
    bool isPalindrome(ListNode* head) 
    {
      ListNode* dummyHead = new ListNode (0 , head);
      dummyHead->next = head;
      ListNode* fast = head;
      ListNode* slow = head;
      // 
      while(fast && fast->next)
      {
        slow = slow->next;
        fast = fast->next->next;
      }
      fast = reverse(slow);
      slow = head;
      while(fast && slow)
      {
        if(fast->val != slow->val)
        {
            return false;
        }
        fast = fast->next;
        slow = slow->next;
      }
      return true;
    }
};

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

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

相关文章

该部署公钥无权限拉代码

从阿里云云效的代码库中执行git pull时报错如下&#xff1a; git pull该部署公钥无权限拉代码 fatal: Could not read from remote repository.Please make sure you have the correct access rights and the repository exists.原因是该代码库在云效上未启用密钥&#xff0c;…

【Material-UI】Select 组件中的 `Auto width`、`Small Size` 和 `Other Props` 详解

文章目录 一、Select 组件概述1. 组件介绍2. Select 组件的基本结构 二、Auto width 属性详解1. Auto width 的作用2. Auto width 属性的基本用法3. Auto width 的实际应用场景 三、Small Size 属性详解1. Small Size 的作用2. Small Size 属性的基本用法3. Small Size 的实际应…

三分钟总结开源流程表单的优势特点

实现流程化办公&#xff0c;可以借助低代码技术平台、开源流程表单的优势特点。作为当前较为理想的平台产品&#xff0c;低代码技术平台凭借够灵活、好操作、可视化界面的优势特点&#xff0c;得到了通信业、医疗、高校等很多行业客户朋友的喜爱与支持。今天一起来看看开源流程…

vue2.0纯前端预览附件方法汇总

vue2.0纯前端预览附件方法汇总 一、使用iframe预览1.使用 Office 在线查看器2.XDOC文档预览服务XDOC官网地址:[https://view.xdocin.com/](https://view.xdocin.com/) 二、vue-office具体效果可以参考: [https://501351981.github.io/vue-office/examples/dist/#/docx](https:/…

linux下使用xargs批量操作

1、创建测试文件&#xff1a; for i in {1..4}; do touch $i.gz; done;2、将所有gz文件重命名为.gz.log2文件 ls | xargs -I {} sh -c mv {} {}.log2 3、将所有.log2文件改回为.gz文件 ls | xargs -I {} sh -c mv {} $(echo {} | sed "s/\.log2//g" ) 4、将所有的…

优先级队列面试题详解

题目链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09;. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/smallest-k-lcci/description/ 解题思路&#x…

新华三H3C HCL配置IS-IS基本配置

实验目标 完成本实验,应该能够达到以下目标。 ●掌握如何在路由器进行单区域IS-IS的基本配置 ●掌握如何在路由器上查看IS-IS路由表、邻居信息 ●掌握如何在路由器上查看IS-IS的LSDB信息 实验拓扑 IP地址表 实验任务 单区域配置&#xff1a; 在本实验任务中,需要在路由器上…

STM32标准库HAL库——MPU6050原理(卡尔曼滤波和DMP库处理数据)和代码

目录 陀螺仪相关基础知识&#xff1a; 陀螺仪数据处理的三种方式&#xff1a; 加速度计&#xff0c;陀螺仪的工作原理&#xff1a; 陀螺仪在智能车中的应用&#xff1a; MPU6050原理图和封装图&#xff1a; ​编辑 硬件IIC和软件IIC的区别&#xff1a; 相同点 不同点 …

MagiskBoot编译解包打包boot.img

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 编译环境准备 1. Windows下启用开发者模式&#xff0c;因为需要 symbolic link 支持 2. 安装 python3.8&#xff0c;并配置PATH环境变量 # 查看python版本信…

kali——sqlmap的使用

目录 前言 sqlmap在kali中的使用 检测注入点 产看所有数据库 查看当前网站使用的数据库 产看数据表 查看字段 查看字段数据 查看数据库用户 查看所有用户 获取数据库用户密码 查看用户权限 判断当前数据库用户是否是管理员 批量自动化扫描 post请求注入 cookie…

html+css+js网页设计 婚庆类型 金夫人婚庆5个页面

htmlcssjs网页设计 婚庆类型 金夫人婚庆5个页面 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 …

时序预测 | 基于WTC+transformer时间序列组合预测模型(pytorch)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 WTCtransformer时间序列组合预测模型 WTC,transformer 创新点&#xff0c;超级新。先发先得&#xff0c;高精度代码。 预测主模型transformer也可以改其他WTC-former系列&#xff0c;比如WTC-informer/autoformer等等…

STM32ADC(逐次逼近型)

1 ADC Analogto DigitalConverter 1 模拟信号转换器数字信号 2 幅度上是离散的&#xff08;也就是把自然界的信息转换成单片机里面的信息&#xff09; 2 12位逐次逼近型ADC 1 采样深度是指&#xff1a;用多少位二进制数来表示一个采样点 ADC的采样深度越深 转…

Prometheus之Blackbox监控

Prometheus之Blackbox监控 部署Blackbox 下载地址 https://github.com/prometheus/blackbox_exporter这里使用amd64架构的软件包&#xff0c;根据自己设备架构选择 右键复制链接地址 下载软件包 wget https://github.com/prometheus/blackbox_exporter/releases/download/v0…

海绵城市雨水监测系统

海绵城市雨水监测系统主要有&#xff1a;数据采集、无线数据传输、后台云服务、终端平台显示等部分组成。系统通过前端数据采集水质&#xff08;ss\cod\浊度、PH等&#xff09;、雨水雨量、流量、水位、土壤湿度、气象等数据。通过无线数据传输通讯&#xff08;4G、5G、以太网、…

电力电容器一般用多久更换

电力电容器的更换周期取决于多个因素&#xff0c;包括其工作环境、使用条件、维护情况等。一般情况下&#xff0c;电力电容器的更换周期可以参考以下几个方面&#xff1a; 一、标准使用寿命 1、普通电力电容器&#xff1a; 使用寿命&#xff1a;通常为 8 到 15 年。这个范围可…

新神器!分享6款毕业AI论文写作推荐免费网站

在当今学术研究和写作领域&#xff0c;AI论文写作工具的出现极大地提高了写作效率和质量。这些工具不仅能够帮助研究人员快速生成论文草稿&#xff0c;还能进行内容优化、查重和排版等操作。以下是六款值得推荐的AI毕业论文开题报告一键生成网站&#xff1a; 一、千笔-AIpassp…

软件测试——测试分类

测试分类 按照测试目标测试 界面测试 页面内展示的所有内容/元素都需要测试 参考UI图找不同 功能测试 ​ 如何设计功能测试用例&#xff1f; 参考产品规格说明书进行用例的编写&#xff0c;具体的测试用例需要使用黑盒设计测 试用例的方法&#xff0c;如等价类、边界值、…

zdppy+vue3+onlyoffice文档管理系统实战 20240829上课笔记 Python验证码框架完成

遗留的问题 还没有测试校验的功能 测试校验验证码的功能 生成验证码 from .tobase64 import get_base64 from .validate import is_captchadef captcha(api, cache, num4, expire60):""":param cache: 缓存对象:param num: 验证码的个数:param expire: 验证…

【cocos creator】养成游戏简易事件系统,每日随机事件,每日行动点重置,根据数据检测多结局

const { ccclass, property } cc._decorator;let actionEvent {EVENT1: { name: "工作", need: { ap: 1 }, cost: { ap: 1 }, award: { coin: 50 }, count: 7, max_count: 5 },EVENT2: { name: "练功", need: { ap: 1 }, cost: { ap: 1 }, award: { atta…