链表OJ--超详细解析

news2024/10/5 14:27:40

链表OJ

文章目录

  • 链表OJ
    • 1. 反转链表
    • 2. 返回K值
    • 3. 链表的中间节点
    • 4. 回文链表
    • 5. 相交链表
    • 6. 带环链表
      • 6.1 为什么一定会相遇,有没有可能会错过,或者出现永远追不上的情况,请证明
      • 6.2 slow一次走一步,fast如果一次走3步,走4步,走5步还能追上吗,请证明
    • 7. 带环链表2
      • 7.1 为什么它们最终肯定会在入口点的位置相遇,请证明
    • 8. 复制链表
    • 结语

1. 反转链表

OJ链接:206. 反转链表 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

思路一:可以新创建一个链表,然后采用头插的方法,从 1 一个接一个的头插数据,这种是最容易想到的方法,但是要开辟一块空间,需要考虑空间复杂度的问题

思路二:使用 n1 n2 n3 三个指针,首先我们让 n1 指向空, n2 指向头节点, n3 指向头节点的下一个节点
在这里插入图片描述

然后我们再让 n1 指向 n2n2 指向 n3 ,就变成了这样

在这里插入图片描述

这样的话 n1 是不是指向反转链表之后的尾节点,那我们如果再让 n1 指向 n2n2 指向 n3 ,把整个链表都给遍历一遍之后,是不是就把整个链表给反转了,对吧,像这样

在这里插入图片描述

然后我们在考虑遍历截止的条件,哎,是不是 n2 为空了之后遍历就截止了,最后再返回 n1 就好了

整体的思路是不是都比较清楚了,接下来就是代码实现了,这个就得多练习了

struct ListNode* reverseList(struct ListNode* head) {
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* n1 = NULL,*n2 = head,*n3 = n2->next;
    while(n2)
    {
        n2->next = n1;
        n1 = n2;
        n2 = n3;
        if(n3)
            n3 = n3->next;
    }
    return n1;
}

2. 返回K值

OJ链接:面试题 02.02. 返回倒数第 k 个节点 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

思路一:我们可以创建一个数组,把链表中的数据存放在数组里,用数组来做,不过要再开辟一块空间,还要考虑空间复杂度的问题

思路二:反转链表遍历一下,再找第K个节点,这样也是可行的

思路三:使用快慢指针的方法,我们先让快指针走K步,然后再让快慢指针一起走,就能发现当快指针指向空的时候,我们的慢指针指向的就是倒数第K个节点,最后返回慢指针节点的值就行了

在这里插入图片描述

代码实现:

int kthToLast(struct ListNode* head, int k){
    struct ListNode* fast = head,*slow = head;
    while(k--)
    {
        fast = fast->next;
    } 
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow->val;
}

3. 链表的中间节点

OJ链接:876. 链表的中间结点 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

思路:主要的思路还是我们的快慢指针,这时就要分两种情况,一个是奇数的情况,另一个是偶数的情况

  • 当链表为奇数时:首先我们要初始化两个快慢指针,然后通常让慢指针走一步,快指针走两步,像这样

在这里插入图片描述

像这样,当快指针指向尾节点的时候,慢指针就刚好指向中间节点,最后返回慢指针节点的值

  • 当链表为奇数时:首先我们还要初始化两个快慢指针,然后还是让慢指针走一步,快指针走两步,像这样
    在这里插入图片描述

就像这样,和前者不同的是,当快指针指向空时,慢指针才指向中间节点,最后返回慢指针节点的值

思路都清楚了吧,接下来就是代码实现:

struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* fast = head,*slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

4. 回文链表

OJ链接:234. 回文链表 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

我们要想证明它是个回文结构,首先我们要先知道回文结构是什么,从前往后和从后往前是完全相同的以中间节点为中心这个链表是对称的,举个例子比如12321和1221这两个都是回文,如果是123123这个还是回文吗,这种就不是回文了

思路:我们可以先找中间节点,找中间节点有什么好处呢,当我们判断链表是否是回文链表,是不是首先寻找中间节点,中间节点断开然后再从两头对应,要是两头节点的值都能一一对应,我们就说这个链表是回文链表,我们找到中间节点之后,然后从中间节点断开,将中间节点后面的链表反转,这样就可以一一比较,怎么比呢,一个从头节点开始,另一个从中间节点开始,一一比较,就可以了,都相等就是回文链表,只要有一个不相等,就不是回文链表

现在我们思考一个问题,需要分情况吗,很简单,画个图就知道了

在这里插入图片描述

代码实现:

 struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* fast = head,*slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}
 struct ListNode* reverseList(struct ListNode* head) {
    if(head == NULL)
    {
        return NULL;
    }
    struct ListNode* n1 = NULL,*n2 = head,*n3 = n2->next;
    while(n2)
    {
        n2->next = n1;
        n1 = n2;
        n2 = n3;
        if(n3)
            n3 = n3->next;
    }
    return n1;
}
bool isPalindrome(struct ListNode* head) {
    struct ListNode* mid = middleNode(head);
    struct ListNode* rmid = reverseList(mid);
    struct ListNode* cur = head; 
    while(rmid)
    {
        if(cur->val != rmid->val)
        {
            return false;
            break;
        }
        cur = cur->next;
        rmid = rmid->next;
    }
    return true;
}

5. 相交链表

OJ链接:160. 相交链表 - 力扣(LeetCode)

好的,我们一起来看一下题目,题目是这样说的

在这里插入图片描述

思路一:我们首先要有一个共识就是,链表相等不一定相交,这个大家一定要清楚这一点,我们认为如果两个链表相交,那么他们的地址是相同的,而不是数据是相等的,像这样,我们需要注意一下

在这里插入图片描述

我们可以一个一个节点进行比较,将链表A的所有节点和链表B的所有节点,但是我们想想时间复杂度是多少,是不是O(n*2),不满足题意

思路二:首先我们要做的是判断两个链表是否相交,再找出交点,我们大致的思路就是这样,那我们应该怎么判断呢,怎么找呢

  • 哎,我们把两个链表都遍历一下,如果它们的尾节点指向的位置相同是不是两个链表就一定会相交,这就解决了如何判断两个链表是否会相交的问题
  • 那我们知道两个链表相交之后,如何找交点呢,我们想一想如果我们让两个指针都指向头节点,然后再同时遍历的话,是不是有可能会错过啊,就像这样

在这里插入图片描述

  • 是不是啊,说的没有问题吧,那怎么办呢,看来大家都很聪明,已经发现了,如果我们让 pb 先走一步,让它们的起始位置都一样,然后再让它们一步一步地走就可以相遇了,像这样

在这里插入图片描述

哦,也就是说,我们先让长一些指针走,走它们的差值步,使两个指针指向的位置相同,再让两个指针一步一步往下走,就可以找到交点了,这个思路还是很清晰的,接下来就是代码实现了

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* pa = headA,*pb = headB;
    int lenA = 1,lenB = 1;
    while(pa)
    {
        pa = pa->next;
        lenA++;
    }
    while(pb)
    {
        pb = pb->next;
        lenB++;
    }
    if(pa != pb)
    {
        return NULL;
    }
    int gap = abs(lenA - lenB);
    //假设法
    struct ListNode* plong = headA,*pshort = headB;
    if(lenA < lenB)
    {
        plong = headB;
        pshort = headA;
    }
    while(gap--)
    {
        plong = plong->next;
    }
    while(plong != pshort)
    {
        plong = plong->next;
        pshort = pshort->next;
    }
    return plong;
}

6. 带环链表

OJ链接:141. 环形链表 - 力扣(LeetCode)

在这里插入图片描述

思路:主要的思路还是我们的快慢指针,也就是让慢指针走一步,快指针走两步,先让两个指针从头节点的位置出发如果带环的话,一定会在环中相遇,否则的话,快指针就先指向尾节点,画个图就能更好的理解

在这里插入图片描述

理顺思路之后,代码就很好写的

bool hasCycle(struct ListNode *head) {
    struct ListNode* fast = head,*slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;

        if(slow == fast)
        {
            return true;
        }
    }
    return false;
}

好了,这个题还是非常简单的,那么在我们面试的时候,面试官可能会问我们两个问题

6.1 为什么一定会相遇,有没有可能会错过,或者出现永远追不上的情况,请证明

证明:假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快指针相遇了,这是最好情况,那最最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动 一次之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇

在这里插入图片描述

6.2 slow一次走一步,fast如果一次走3步,走4步,走5步还能追上吗,请证明

证明:我们假设 fast 一次走3步,当 slow 进环时,和 fast 距离是N,追击过程距离变化如下图所示:

在这里插入图片描述

我们可以很明显的看到,当N为偶数时,就能追击上,当N为奇数时,最后值为-1就表明已经错过了,要开始进行新一轮的追击

接下来我们在考虑新一轮追击距离变成了多少,是不是C-1(假设C为环的长度)

我们想一想是不是也要分两种情况当C-1为偶数时,就能追击上,当C-1为奇数时,最后值还是为-1,就永远追不上

小结一下:

  1. N是偶数,第一轮就追上了

  2. N是奇数,第一轮追击会错过,距离变成C-1

    a. 如果C-1是偶数,下一轮就追上了

    b. 如果C-1是奇数,那么就永远追不上

那我们在思考一下,b成立的条件,是不是永远就追不上呢,我们发现当N为奇数且C为偶数时,结论成立,我们刚在是逻辑上想的,接下来证明一下,看看是不是就永远追不上

假设slow进环时,fast与slow的距离是N

假设slow进环时,fast已经走了x圈

设slow走过的距离为 L

fast走过的距离就是 L+x*C+C-N

那么我们又说了,fast走的距离是slow距离的3倍

就可以非常轻松地写出 3L = L+x*C+C-N

化简一下就是 *2L = (x-1)C-N

哦,这就非常明显了,等式左边一定是偶数,那就说明等式右边也一定是偶数

  • 我们说当N为偶数的话,那么C只能为偶数,(x-1)*C才为偶数
  • 当N为奇数的话,那么C只能为奇数,只有奇数-奇数才能是偶数

那就说明当N为奇数且C为偶数时,条件不成立,所以永远追不上的假设不成立,一定会追上的

结论

  • N是偶数,第一轮就追上了
  • N是奇数,第一轮追不上,C-1是偶数第二轮就可以追上

7. 带环链表2

OJ链接:142. 环形链表 II - 力扣(LeetCode)

在这里插入图片描述

思路:主要的思路还是我们的快慢指针,slow走一步fast走两步,当它们在环里相遇时,让一个指针从链表起始位置开始遍历链表,同时让一个指针从判环时相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。

思路很简单,看看代码:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow = head,*fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //相遇点
        if(slow == fast)
        {
            struct ListNode* meet = slow;
            while(meet != head)
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
}

通常这个时候面试官会问一个问题,这个问题和我们一样

7.1 为什么它们最终肯定会在入口点的位置相遇,请证明

证明:假设slow和fast已经相遇了,设初始位置到入口点的距离为 L

设入口点到相遇点的距离为 N ,环的长度为 C

假设slow相遇时,fast已经走了x圈

那么fast走过的距离为 L+x*C+N

slow走过的距离为 L+N

思考一个问题我们slow与fast相遇的时候,slow一定走不完一圈

很简单,如果slow走完一圈的话,fast是不是就走完两圈了,是不是早就可以相遇了

fast走过的距离是slow走过距离的两倍

也就是 **L+x * C+N = 2 * (L+N) **

化简一下也就是 *L = (x-1)C + C-N

假如x=1,那么L=C-N,正好是相遇点到进环点的距离与入环之前的距离相等,让一个节点从头开始遍历,一个从相遇点开始,最终在入环的第一个节点相遇,证明了我们肯定会在入口点的位置相遇,如果x不唯1呢,那也会相遇,无非就是在环里多走了几圈,最后还是会在入环的第一个节点相遇

对大家来说这些都很EZ,对不对,接下来的最后一道OJ题就是比较综合的了

8. 复制链表

OJ链接:138. 随机链表的复制 - 力扣(LeetCode)

在这里插入图片描述

题干中提到了一个深拷贝的概念,深拷贝是拷贝一个值和指针指向跟当前链表一模一样的链表

思路一:这道链表题每个节点里多了个指针指向随机节点,也有可能指向空,然后深拷贝一份,如果我们直接遍历然后拷贝呢?硬拷贝是可以的,但是有个问题,随机指针(random)指向的值如何在新链表中实现呢,如果我们去链表中查找,那么万一有重复的值怎么办呢,这就会导致没拷贝成功,如果真要用这种暴力查找,就要算相对位置,而且它的时间复杂度为O(N^2),还需要考虑时间复杂度的问题

思路二:我们这个题的主要思路应该是先拷贝链表,然后把拷贝的链表插入到原节点的后面,每个拷贝节点和原节点建立了一个关联关系

在这里插入图片描述

接下来就是要考虑 random 的问题了,如下图所示,我们7的 random 指向的为空,那么我们拷贝节点7的 random 也要指问空,我们拷贝节点7就在原节点的后面,所以处理起来很方便,那么13的 random 指向的是7我们如何拷贝呢,拷贝节点的random指向的就是 cur->random>next ,最后再将拷贝节点拿下来尾插,成为一个新的链表,虽然我们破坏了原链表的结构,我们可以选择恢复原链表也可以不恢复,就要看看题目的要求

在这里插入图片描述

代码如下:

typedef struct Node Node;
struct Node* copyRandomList(struct Node* head) {
    Node* cur = head;
    // 拷贝节点插入在原节点后面
    while (cur)
    {
        Node* copy = (Node*)malloc(sizeof(Node));
        copy->val = cur->val;
        copy->next = cur->next;
        cur->next = copy;
        cur = copy->next;
    }
    //控制random
    cur = head;
    while (cur)
    {
        Node* copy = cur->next;
        if (cur->random == NULL)
        {
            copy->random = NULL;
        }
        else
        {
            copy->random = cur->random->next;
        }
        cur = copy->next;
    }
    //拷贝节点取下来尾插成为新链表,然后恢复原链表(不恢复也可以)
    Node* copyhead = NULL, * copytail = NULL;
    cur = head;
    while (cur)
    {
        Node* copy = cur->next;
        Node* next = copy->next;
        if (copytail == NULL)
        {
            copyhead = copytail = copy;
        }
        else
        {
            copytail->next = copy;
            copytail = copytail->next;
        }
        //恢复原链表
        cur->next = next;
        cur = next;
    }
    return copyhead;
}

结语

好了,感谢你能看到这里,本篇到这就结束了,希望对你有所帮助

溜了溜了,我们下篇文章再见吧

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

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

相关文章

读书笔记-《人人都是产品经理》

在开发工程师与产品经理的段子中&#xff0c;常看到“人人都是产品经理”这句话&#xff0c;用来调侃这个岗位似乎没有什么门槛。 很明显&#xff0c;这句话的出处&#xff0c;即本书作者想表达的是每个人都可以运用产品思维去解决问题。 01 产品 产品&#xff1a;用来解决某…

SpringMVC的使用

SpringMVC详情 RequestMapping("/hello") 负责用户的请求路径与后台服务器之间的映射关系 如果请求路径不匹配,则用户报错404 ResponseBody 作用: 将服务器的返回值转化为JSON. 如果服务器返回的是String类型,则按照自身返回. 新增: post请求类型 PostMapping("…

OKR:2024年目标和关键成果常见问题

什么是目标和关键结果&#xff08;OKR&#xff09;&#xff1f; 目标和关键结果&#xff08;#OKR#&#xff09;是一种由结果驱动的目标制定方法。在企业中&#xff0c;OKR经常被用来指导基于结果的成功。使用结果而不是任务作为驱动力&#xff0c;OKRs 鼓励通过度量指标对实现成…

LLM 理论知识

LLM 理论知识 一.大型语言模型LLM1.1 大型语言模型 LLM 的概念1.2 常见的 LLM 模型1.2.1 闭源 LLM (未公开源代码)1.2.1.1 GPT 系列1.2.1.1.1 ChatGPT1.2.1.1.2 GPT-4 1.2.1.2 Claude 系列1.2.1.1.3 PaLM/Gemini 系列1.2.1.1.4 文心一言1.2.1.1.5 星火大模型 1.2.2. 开源 LLM1.…

MapStruct对象转换

MapStruct是一个Java注解处理器&#xff0c;用于简化对象的转换 遇到的问题&#xff1a; java: Internal error in the mapping processor: java.lang.NullPointerException 解决方案&#xff1a;修改编辑器配置 -Djps.track.ap.dependenciesfalse

超长国债来了,高净值客群的机会在哪儿?

有人说&#xff0c;2024年是全球经济的“分化年”&#xff0c;也是中国经济突围的“关键年”。当前&#xff0c;我国经济恢复仍处在关键阶段&#xff0c;长期向好的基本趋势没有改变&#xff0c;但也需要克服一些挑战&#xff0c;而巩固和增强经济复苏的良好势头&#xff0c;离…

转行AI的人,都后悔了

说真的&#xff0c;很多转行AI的人&#xff0c;都后悔了&#xff0c;后悔没早点转&#xff0c;因为AI岗实在是太香了&#xff01;就连Google都开始捞****AI/ML的人才了&#x1f447; 曾经在一家IT外企研发中心做仿真工程师的小夏&#xff0c;每天加班、薪资还只有150k&#xff…

Codeforces Round 923 (Div. 3)(A~D题解)

目录 A. Make it White B. Following the String C. Choose the Different Ones! D. Find the Different Ones! A. Make it White Problem - A - Codeforces 题意&#xff1a;问在一个只含有W和B元素的字符串中&#xff0c;选择一个L到R范围&#xff0c;将之间包含的B全部变…

RK3568技术笔记八 开发环境的搭建

先按照前面的方法将 ubuntu 安装在 PC 机上。 编译开发Linux系统&#xff0c;虚拟机Ubuntu 系统要求&#xff1a; 64位系统&#xff0c;硬盘空间大于或等于200G&#xff0c;内存不小于6GB。 建议使用 Ubuntu18.04 系统进行编译。 光盘资料&#xff1a;SAIL-RK3568开发板光盘…

Ceph: vdbench 测试ceph存储rbd块设备

目录 2.搭建ceph 3.vdbench环境 准备 笔记本架构&#xff1a;x86 i5 8 代 虚拟化软件&#xff1a;virtualBox 7.0 操作系统&#xff1a;CentOS Linux release 7.9.2009 (Core) 测试虚拟机操作系统&#xff1a;CentOS Linux release 7.9.2009 (Core) 节点 外部网络 内部网…

langchain教程-(1)Prompt模板

LangChain 的核心组件 模型 I/O 封装 LLMs&#xff1a;大语言模型Chat Models&#xff1a;一般基于 LLMs&#xff0c;但按对话结构重新封装PromptTemple&#xff1a;提示词模板OutputParser&#xff1a;解析输出 数据连接封装 Document Loaders&#xff1a;各种格式文件的加载…

C# yolov8 OpenVINO 同步、异步接口视频推理

C# yolov8 OpenVINO 同步、异步接口视频推理 目录 效果 项目 代码 下载 效果 同步推理效果 异步推理效果 项目 代码 using OpenCvSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Windows.Form…

YOLOv10改进 | 主干篇 | YOLOv10引入FasterNeT替换Backbone

1. FasterNeT介绍 1.1 摘要: 为了设计快速神经网络,许多工作一直致力于减少浮点运算(FLOP)的数量。 然而,我们观察到,FLOP 的减少并不一定会导致延迟的类似程度的减少。 这主要源于每秒浮点运算 (FLOPS) 效率低下。 为了实现更快的网络,我们重新审视流行的算子,并证明…

点击即转换,Mistune库助你驾驭Markdown文档!

mistune 是一个用于将 Markdown 文本解析为 HTML 的 Python 库.它提供了快速、简单的方法来处理 Markdown 格式的文本,并将其转换为 HTML,适用于将 Markdown 文档集成到网站、博客等项目中. 安装 #可以使用 pip 来安装 mistune&#xff1a;pip install mistune示例 基本用法…

《SelectDB 新一代日志存储分析平台解决方案》白皮书重磅发布|立即下载

随着信息技术的飞速进步&#xff0c;企业面临着前所未有的系统复杂性和数据挑战。在此背景下&#xff0c;日志数据成为了企业洞察系统内部状态、监控网络安全以及分析业务动态的宝贵资源&#xff0c;构建高效的日志存储与分析平台至关重要。 作为基于 Apache Doris 打造的现代…

计算机网络:运输层 - 概述

计算机网络&#xff1a;运输层 - 概述 运输层的任务端口号复用与分用UDP协议首部格式 TCP协议面向字节流 运输层的任务 物理层、数据链路层以及网络层&#xff0c;他们共同解决了将主机通过网络互联起来所面临的问题&#xff0c;实现了主机到主机的通信。 网络层的作用范围是…

CVE-2011-1473: OpenSSL权限许可和访问控制问题漏洞及解决方案

CVE-2011-1473: OpenSSL权限许可和访问控制问题漏洞 漏洞详情&#xff1a; OpensSL.是OpensSL团队的一个于源的能够实现安全套接层&#xff08;SSL2/3&#xff09; 和安全传输层&#xff08;TLSw1&#xff09;协议的通用加密库。该产品支持多种加密算法&#xff0c;包括对称密…

.NET周刊【6月第3期 2024-06-18】

国内文章 记一次 .NET某游戏币自助机后端 内存暴涨分析 https://www.cnblogs.com/huangxincheng/p/18243233 文章讨论了程序中非托管内存暴涨的问题。作者描述了友人发现内存问题并请他帮助分析的背景&#xff0c;利用WinDbg工具分析Linux平台上的内存泄漏情况。文章介绍了如…

Gotchi 战士们准备好吧!稀有度挖矿第八季锦标赛即将开始!

我们很高兴地宣布稀有度挖矿第 8 赛季的比赛即将开始&#xff0c;比赛将设立 15 万 GHST 的巨额奖金池&#xff0c;同时还将进行新的更新&#xff0c;让您有更多的方式来制定战略并与您的小鬼好友们一较高下。 本赛季引入了双败淘汰赛&#xff0c;每支队伍可以有两名替补队员&a…

AI办公自动化:批量根据Excel表格内容制作Word文档

工作任务&#xff1a;Excel表格中有大量文本&#xff0c;根据这些文本自动生成word文档 在chatgpt中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;写一个Python脚本&#xff0c;具体步骤如下&#xff1a; 读取Excel文件&#xff1a;"F:\AI自媒体内容\AI视…