链表OJ刷题(二)

制作不易,三连支持一下呗!!!

文章目录

  • 前言
  • 一、链表的回文结构
  • 二、相交链表
  • 三、链表中倒数第k个节点
  • 四、环形链表Ⅰ和Ⅱ
  • 总结


前言


一、链表的回文结构

链表的回文结构_牛客题霸_牛客网

这里我们需要先了解一下什么叫做回文:从前向后看与从后向前看的结果是一样的,我们就称为回文结构!!!

思路: 这道题目是我们之前链表OJ(一)中两道经典题目的结合------查找中间节点与链表的逆置。

我们可以先找到链表的中间节点(如果链表节点个数为偶数,有两个中间节点,我们选取靠后的那一个),然后将链表的中间节点以后的节点全部逆置。最后循环遍历原链表的前半部分和逆置得到的后半部分是否相同,如果相同就是回文结构,反之就不是。

 我们只需要将这两个步骤分别实现成函数来调用即可

代码实现:

struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};
typedef struct ListNode ListNode;
//查找中间节点
struct ListNode* middleNode(struct ListNode* head) {
    ListNode* pfast,*pslow;
    pfast=pslow=head;
    while(pfast&&pfast->next)
    {
         pfast=pfast->next->next;
         pslow=pslow->next;
    }
    return pslow;
}
//逆置所传链表
struct ListNode* reverseList(struct ListNode* head) {
    if(!head)
    {
        return head;
    }
    ListNode*prev,*pcur,*next;
    prev=NULL;
    pcur=head;
    next=head->next;
    while(pcur)
    {
        pcur->next=prev;
        prev=pcur;
        pcur=next;
        if(next)
        next=next->next;
    }
    return prev;
}
class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) {
        // write code here
        if(!A)
        return true;
     ListNode* mid=middleNode(A);
     mid= reverseList(mid);
     //循环遍历两个链表,进行比较
     while(mid)
     {
        if(A->val!=mid->val)
        return false;
        mid=mid->next;
        A=A->next;
     }
     return true;
    }
};

二、相交链表

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

 思路一:计算两个链表的长度

 步骤:

1.先判断是否相交:

如果两个链表的尾节点相同则一定是相交的,如果连尾节点都不同,则一定不相交。

2.找到相交的起始节点:

先分别通过循环遍历的方式计算出两个链表的长度lenA和lenB,用gap来表示它们之间的差值,让长链表先遍历gap步,以保证此时链表的长度是相同的,最后循环遍历两个链表,如果出现链表A和链表B的节点是相同的,则表示这个节点就是我们要找的节点。

代码实现:

 typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    int lenA=0,lenB=0;
    ListNode* curA=headA,*curB=headB;
    //计算链表A的长度
    while(curA)
    {
        lenA++;
        curA=curA->next;
    }
    //计算链表B的长度
     while(curB)
    {
        lenB++;
        curB=curB->next;
    }
    //判断尾节点是否相同
    if(curA!=curB)
    {
    return NULL;
    }
    int gap=abs(lenA-lenB);
    ListNode* longlist=headA;
    ListNode* shortlist=headB;
    if(lenB>lenA)
    {
        longlist=headB;
        shortlist=headA;
    }
    //让长链表先走gap步
    while(gap--)
    {
        longlist=longlist->next;
    }
    //循环找相交起始节点
    while(longlist!=shortlist)
    {
        longlist=longlist->next;
        shortlist=shortlist->next;
    }
    return longlist;
}

思路二: 走相同的路,彼此一定会相遇

除了计算链表长度外,我们可以通过两个链表长度和相同的性质来解题

过程:遍历其中一个链表,当到末尾时跳到另一个链表继续遍历,直到再次到达末尾

1.如果链表不相交,则两个遍历指针同时指向NULL,返回NULL即可。

2.如果链表相交,因为两个遍历指针同时达到末尾,向前推理可知,两个指针一定会有同时指向相交的起始节点的时候。

3.如果两个链表等长,也会同时到达相交的起始节点。

代码实现:

这个思路实现起来就比较简洁,但是不容易想到!!! 

三、链表中倒数第k个节点 

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路一:求出链表长度,确定所求节点是第几个节点

思路非常简单

代码实现:

思路二:双指针法

受链表的中间节点一题的启发,我们这里依旧可以使用快慢指针,只需要将快慢指针的距离控制在k,当快指针走到链表末尾时,慢指针正好到达所求节点。

代码实现:

四 、环形链表Ⅰ和Ⅱ

环形链表Ⅰ:

https://leetcode.cn/problems/linked-list-cycle/

思路: 快慢指针法

定义一个快指针,一个慢指针,快指针一次走两步,慢指针一次走一步。如果此链表带环则,快慢两个指针一定会相遇,如果此链表不带环,则fast指针或fast->next最后一定会是NULL。

代码实现:

typedef struct ListNode ListNode;
bool hasCycle(struct ListNode *head) {
    ListNode* fast=head,*slow=head;
    while(fast&&fast->next)//注意:顺序不能调换,否则可能会存在短路的情况
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)
        {
            return true;
        }
    }
    return false;
}

接下来我们从数学的角度解释为什么两个指针一定会相遇:

 

假设上图是一个带环链表 ,当slow也进环时,假设fast与环的入口之间的距离是X,环的长度是C

由于两个指针的速度差是1 ,总有一天fast指针会追上slow指针,并不会错过,并且此时slow指针一定还没有走够一圈(因为X<C),所以我们就证明了一个指针走两步,一个指针走一步一定可以追上!!!


同理 如果一个指针走三步,一个指针走一步,我们也可以用相同的思路来验证是否一定会追上:

答案是一定会。

假设slow进环时两个指针的位置如上图所示。 

一.假设此时两个指针的距离N为偶数,则因为每走一步两个指针的距离缩小2,那么在第一轮fast一定可以追上slow(并且此时slow并未转够一圈)!!!

二.如果N为奇数,则第一圈并不能追上,则两个指针的距离在经过第一轮追击之后会变为C-1(C为环的长度)。

1.如果C-1是偶数,则下一轮就一定可以追上。

2.如果C-1是奇数,则永远也追不上!!!

问题是:N为奇数和C-1是奇数是否可以同时成立呢?

我们假设未进环时slow走的距离是L,slow刚进环时slow和fast的距离是N,环的长度是C,fast已经转了x圈。

slow走的距离是:L

fast走的距离是:L+x*C+N

因为fast的速度是slow的3倍,则3L=L+x*C+N

推出:2L=x*C+N

2L一定是偶数,如果C-1和N同时为奇数,则x*C一定是偶数,x*C+N就一定是奇数,两边不可能相等,矛盾!!!

因此只要N为奇数时,fast一定可以在第二轮追击中追上slow

也就是说,不管如何,fast一定能够追上slow!!!

但是由于fast走两步,slow走一步写起来更简单,我们选择这种方法。


总结

环形链表的逻辑推理很重要!!!

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

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

相关文章

Rocky Linux 运维工具 dnf

一、dnf的简介 dnf​是用于在基于RPM包管理系统的包管理工具。用户可以通过 ​yum​来搜索、安装、更新和删除软件包&#xff0c;自动处理依赖关系&#xff0c;它是yum的继任者&#xff0c;旨在提供更快速、更现代化的软件包管理体验。。 二、dnf 的参数说明 序号参数描述1in…

django项目 法律法规管理系统

1.项目结构 2.项目需求 1.用户管理模块 2.数据采集模块 3.知识管理模块 4.智能匹配模块 5.个人收藏模块 6.数据分析模块 7.页面展示模块 3.知识点 1.智能匹配模块推荐算法的实现原理 TF (Term Frequency)&#xff1a;词频&#xff0c;表示一个词在文档中出现的频…

LeetCode --- 长度最小的子数组(滑动窗口)

前言 滑动窗口算法是一种用于解决数组或者列表中子数组或者字串问题的方法&#xff0c;通常用于在给定数据上执行连续区间的操作&#xff0c;算法基本思想是维护一个固定大小或不定大小的窗口&#xff0c;通过移动窗口的起始位置和结束位置来遍历整个数据。在每个窗口位置&…

重拾前端基础知识:JavaScript

重拾前端基础知识&#xff1a;JavaScript 前言使用JavaScript输出语法运算符条件语句循环数据类型字符串数字数组对象日期函数 数学正则表达式异常处理类集合模块JSON闭包异步调试DOM&#xff08;文档对象模型&#xff09;事件事件监听器表单 BOM&#xff08;浏览器对象模型&am…

【Linux杂货铺】调试工具gdb的使用

目录 &#x1f308;前言&#x1f308; &#x1f4c1;背景介绍 &#x1f4c1; 使用 list [行号] / [函数名] run/r break/b [行号] / [函数名] info break disable break enable break delete break [断点编号] next/n step/s continue/c finish print/p [变量…

旧的Spring Security OAuth已停止维护,全面拥抱新解决方案Spring SAS

Spring Authorization Server 替换 Shiro 指引 背景 Spring 团队正式宣布 Spring Security OAuth 停止维护&#xff0c;该项目将不会再进行任何的迭代 目前 Spring 生态中的 OAuth2 授权服务器是 Spring Authorization Server 已经可以正式生产使用作为 SpringBoot 3.0 的最新…

Redis--事务机制的详解及应用

Redis事务的概念&#xff1a; Redis事务就是将一系列命令包装成一个队列&#xff0c;在执行时候按照添加的顺序依次执行&#xff0c;中间不会被打断或者干扰&#xff0c;在执行事务中&#xff0c;其他客户端提交的命令不可以插入到执行事务的队列中&#xff0c;简单来说Redis事…

Springboot接口参数校验

在设计接口时我们通常需要对接口中的非法参数做校验&#xff0c;以降低在程序运行时因为一些非法参数而导致程序发生异常的风险&#xff0c;例如登录的时候需要校验用户名密码是否为空&#xff0c;创建用户的时候需要校验邮件、手机号码格式是否准确。如果在代码中对接口参数一…

AOP案例(黑马学习笔记)

需求 需求&#xff1a;将案例中增、删、改相关接口的操作日志记录到数据库表中 ● 就是当访问部门管理和员工管理当中的增、删、改相关功能接口时&#xff0c;需要详细的操作日志&#xff0c;并保存在数据表中&#xff0c;便于后期数据追踪。 操作日志信息包含&#xff1a; ●…

基于HT32的智能家居demo(蓝牙上位机)

参加合泰杯作品的部分展示&#xff0c;基于HT32的智能家居&#xff0c;这里展示灯光的相关控制&#xff0c;是用蓝牙进行的数据透传&#xff0c;参考了一些资料&#xff0c;美化封装了一下之前的上位机界面。 成果展示 点击主界面的蓝牙设置&#xff0c;进行连接&#xff0c;下…

Android和Linux的嵌入式开发差异

最近开始投入Android的怀抱。说来惭愧&#xff0c;08年就听说这东西&#xff0c;当时也有同事投入去看&#xff0c;因为恶心Java&#xff0c;始终对这玩意无感&#xff0c;没想到现在不会这个嵌入式都快要没法搞了。为了不中年失业&#xff0c;所以只能回过头又来学。 首先还是…

编码规则转换

思考&#xff1a; 如何将一个机内码转换为区内码&#xff1f; 只要将机内码减去 A0A0 就可以啦 如果只让我们用加法器来解决呢&#xff1f; 注意我们的数据占用了 32 位&#xff0c;如果想用补码进行减法运算的话&#xff0c;符号位怎么办&#xff1f;&#xff1f;&#xf…

了解Spring中Bean:配置与作用域

作为一名对技术充满热情的学习者&#xff0c;我一直以来都深刻地体会到知识的广度和深度。在这个不断演变的数字时代&#xff0c;我远非专家&#xff0c;而是一位不断追求进步的旅行者。通过这篇博客&#xff0c;我想分享我在某个领域的学习经验&#xff0c;与大家共同探讨、共…

Linux和Windows集群中部署HTCondor

目录 1、集群架构 2、HTCondor版本 3、Linux系统安装 3.1、HTCondor安装 3.2、中央管理节点配置 3.3、其他节点配置 4、Windwos系统安装 5、安全配置 6、参考 1、集群架构 操作系统IP地址1*Ubuntu22.04192.168.1.742Ubuntu22.04192.168.1.603Ubuntu22.04192.168.1.6…

python3装饰器

装饰器 它允许你修改函数或类的行为&#xff0c;而不更改其源代码。实质上&#xff0c;装饰器是接受另一个函数作为参数并返回一个包装原始函数的新函数。这样&#xff0c;你可以在不修改原始函数的情况下&#xff0c;添加一些额外的功能或逻辑。 def time_cost(func):"…

Java 数组(详细)

目录 一、数组的概述 1. 数组的理解&#xff1a; 2. 数组相关的概念&#xff1a; 3. 数组的特点&#xff1a; 4. 数组的分类&#xff1a; 5.数据结构&#xff1a; 二、一维数组 1. 一维数组的声明与初始化 2. 一维数组元素的引用&#xff1a; 3. 数组的属性&#xff1…

期货开户金融期货的种类

金融期货概念及其种类有哪些&#xff1f;期货种类分为商品期货、金融期货、和期货期权。金融期货是期货的其中一个种类&#xff0c;它是以证券&#xff1b;货币、汇率&#xff0c;利率等金融产品作为买卖标的的期货品种。金融期货交易产生于本世纪70年代的美国市场&#xff0c;…

项目解决方案: 实时视频拼接方案介绍(中)

目 录 1.实时视频拼接概述 2.适用场景 3.系统介绍 4. 拼接方案介绍 4.1基于4K摄像机的拼接方案 4.2采用1080P平台3.0 横向拼接 4.2.1系统架构 4.2.2系统功能 4.2.3方案特色 4.2.4适用场景 4.2.5设备选型 4.3纵横兼顾&#xff0c;竖屏拼接 4.3.1系统…

从下一代车规MCU厘清存储器的发展(2)

目录 1.概述 2.MCU大厂的选择 2.1 瑞萨自研STT-MRAM 2.2 ST专注PCM 2.3 英飞凌和台积电联手RRAM 2.4 NXP如何计划eNVM 3.小结 1.概述 上篇文章&#xff0c;我们简述了当前主流的存储器技术&#xff0c;现在我们来讲讲各大MCU大厂的技术选择 2.MCU大厂的选择 瑞萨日…

取送货问题(Pickup and Delivery Problem)

取送货问题及其变体 广义取送货问题&#xff08;General Pickup and Delivery Problems&#xff0c;GPDP&#xff09;可以分为两类&#xff1a; Vehicle Routing Problems with Backhauls&#xff0c;VRPB&#xff1a;从配送中心&#xff08;depot&#xff09;取货运输货物到客…
最新文章