链表算法题(OJ刷题超详细讲解)

1.返回倒数第K个节点,

OJ链接:返回倒数第K个节点

本题有很多种解法,例如创建数组,或者将原链表反转等等,这里们使用快慢指针,只需要遍历一遍链表,并且空间复杂度为O(1),时间复杂度为O(N)

我们先来画图分析一下 

我们让快指针fast先走k步,然后再一起走,最后返回slow的值。

代码如下:

typedef struct ListNode ListNode;

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

}

2.链表的回文结构

OJ链接:链表的回文结构

本题要求时间复杂度为O(n),额外空间复杂度为O(1),该怎么写呢?

分析:

我们要想证明它是个回文结构,首先我们要先知道回文结构是什么,从前往后和从后往前是完全相同的,以中间节点为中心这个链表是对称的,举个例子比如12321和1221这两个都是回文,如果是123123这个还是回文吗?这种就不是回文了,如果需要证明这个链表是回文,那么我们是不是就可以先找到中间节点,然后再反转一下中间节点及之后的节点,如上图我们找到中间节点2之后把中间节点及之后的节点都反转了,然后让中间节点与首节点进行判断值是否相等,万一是奇数个节点呢,奇数个也没事我们左边的2并没有改变它的next所以2指向的还是3,3和3自己比,最后3指向的就是NULL了。

代码如下:

//找中间节点
struct ListNode* middleNode(ListNode* head)
{
    struct ListNode* slow = head,*fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}
//反转
struct ListNode *reverseList(ListNode *head)
{
    struct ListNode *cur = head;
    struct ListNode* newhead = NULL;
    while(cur)
    {
       struct ListNode*next = cur->next;
        cur->next = newhead;
        newhead = cur;
        cur = next;
    }
    return newhead;
}

    bool chkPalindrome(ListNode* A) {
        // write code here

        struct ListNode*mid = middleNode(A);
        struct ListNode*rmid = reverseList(mid);
        while(rmid && A)
        {

       
            if(A->val != rmid->val)
                return false;
            
            A = A->next;
            rmid = rmid->next;
        
        }
    return true;
}
};

3.相交链表

OJ链接:相交链表

判断链表是否相交,如果说我们一个一个比较的话,A链表的节点依次跟B链表所有节点比较,A某个节点跟B某个节点相等,这个节点就是交点,这样太麻烦了,而且时间复杂度为O(N^2),那么我们可以先遍历链表,判断两个链表的尾节点是否相等,如果相等那么就一定相交,在我们遍历链表的时候,我们可以顺便计算出链表的相对值,然后让长的链表先走差值的距离,在同时走,只要两个节点相等就是相交

画图演示:

代码如下:

 typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    ListNode*curA = headA,*curB = headB;
    int lenA = 1,lenB = 1;
    while(curA->next)
    {
        curA = curA->next;
        ++lenA;
    }
    while(curB->next)
    {
        curB = curB->next;
        ++lenB;
    }
    //尾节点不相等就是不相交
    if(curA->next != curB->next)
    {
        return NULL;
    }
    //长的先走差距步,在同时走,第一个相等就是相交
    //假设法
    int gap = abs(lenA-lenB);//求绝对值
    ListNode* longList = headA,*shortList = headB;
    if(lenB > lenA)
    {
        longList = headB;
        shortList = headA;
    }
    while(gap--)
    {
        longList = longList->next;
    }
    while(longList != shortList)
    {
        longList = longList->next;
        shortList = shortList->next;
    }
    return shortList;
}

如上代码中我们用到了假设法,如果我们假设错了,就修正一下,A长还是B我们不关注,我们只要知道longList是长的shortList是短的就行,假设法可以使得我们逻辑变得更加简单。

注:我们一定要用地址进行判断,如果用值判断可能会有重复的值


4.随机链表的复制

OJ链接:随机链表的复制

深拷贝就是拷贝一个值和指针指向跟当前链表一模一样的链表

这道链表题每个节点里多了个指针指向随机节点,也有可能指向空,然后深拷贝一份,如果我们直接遍历然后拷贝呢?硬拷贝是可以的,但是有个问题,随机指针(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;
}

5.环形链表

OJ链接:环形链表

思路:快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始运行,如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾。

画图演示:

 代码如下:

bool hasCycle(struct ListNode *head) {
    struct ListNode*slow = head,*fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            return true;
        }
    }
    return false;

}

在面试中面试官可能会问的两个问题,如下

1.为什么一定会相遇,没有可能会错过,永远追不上?请证明 

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

2.slow一次走一步,fast走3步4步5步n步能追上吗?请证明

证明如下:

这里我们假设fast一次走3步,当slow进环时,fast开始追击slow,过程中距离变化如下:

偶数                        奇数

N                              N

N-2                           N-2

N-4                           N-4

....                             ....

4                                3

2                                1

1                                -1

0                

每追击一次,距离缩小2

0就是追上了

追成-1就是刚好错过,开始新一轮的追击了

距离变成C-1,(假设C是环的长度)

总结一下:

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

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

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

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

我们在来分析一下会不会出现永远追不上的可能,上面的b条件成不成立呢?

永远追不上是有条件的,同时存在N是奇数且C是偶数,那么就永远追不上

那么有没有可能这两个条件不存在?证明一下

证明如下:

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

slow走的距离是L

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

slow进环时,假设fast已经在环里转了x圈

fast走的距离是slow的3倍

3*L= L+x*C+C-N

化简一下变成:2*L = (x+1)*C-N

偶数 = (x+1)*偶数-奇数

N是奇数时,C也是奇数

N是偶数时,C也是偶数

只有奇数-奇数才能变成偶数

(x+1)*偶数一定是偶数

所以N是奇数且C的偶数不能同时存在,永远追不上的条件不成立

结论:

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

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


6.环形链表II

OJ链接:环形链表II

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

在我们面试的时候面试官可能会问这样的问题,为什么它们最终肯定会在入口点的位置相遇呢?请证明一下?

证明如下:

假设入环前的这段距离是L

假设相遇点到入环点的距离是N

长的环度是C

假设x是fast在环里转了x圈(x>=1)

相遇时:

slow走的路程:L+N

fast走的路程:L+x*C+N

fast走的路程是slow2倍

2*(L+N)=L+x*C+N

L+N = x*C

L=x*C-N

最终化简成这样:L=(x-1)*C+C-N

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

代码如下:

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

总结

经过这次对链表算法题的深入解析,链表作为基础数据结构,其相关算法是编程能力的试金石。通过在线刷题平台,我们可以不断挑战自我,深化对链表的理解,提高算法设计能力。

刷题能够让我们更加熟悉各种算法和数据结构,掌握它们的基本操作和应用场景。通过大量的实践,我们能够加深对理论知识的理解,形成自己的编程风格和思维方式。

刷题不是目的,应用才是关键。让我们通过刷题,不断提升编程水平,将所学知识应用于实际项目中,成为更优秀的开发者。

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

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

相关文章

【C++杂货铺】unordered系列容器

目录 🌈 前言🌈 📁 unordered系列关联式容器 📁 底层结构 📂 哈希概念 📂 哈希冲突 📂 哈希函数 📂 哈希冲突解决 📁 模拟实现 📁 总结 🌈 前…

Tailwindcss Layout布局相关样式及实战案例,5万字长文,附完整源码和效果截图

aspect 相关样式类 基础样式 ClassPropertiesaspect-autoaspect-ratio: auto;aspect-squareaspect-ratio: 1 / 1;aspect-videoaspect-ratio: 16 / 9; 案例:引入B站视频 Use the aspect-* utilities to set the desired aspect ratio of an element. 使用’ asp…

k8s学习--ConfigMap详细解释与应用

文章目录 一 什么是configmapConfigMap 的好处ConfigMap 的限制 二.创建ConfigMap的4种方式1.在命令行指定参数创建2.在命令行通过多个文件创建3.在命令行通过文件提供多个键值对创建4.YAML资源清单文件创建 三 configmap的两种使用方法1.通过环境变量的方式传递给pod2.通过vol…

Java(十二)---认识异常

文章目录 前言1. 异常的概念与体系结构1.1.异常的概念1.异常的体系1.3 异常的分类 2. 异常的处理2.1 防御式编程2.2 异常的抛出2.3 异常的捕获2.3.1 异常声明throws2.3.2 try-catch捕获并处理2.3.3 finally 2.4 异常的处理流程 3. 自定义异常类 前言 这一篇就是咱们学习JavaSE…

学习笔记——网络参考模型——TCP/IP模型(传输层)

四、TCP/IP模型-传输层 一、TCP 1、TCP定义 TCP(Transmission Control Protocol,传输控制协议)∶为应用程序提供可靠的面向连接的通信服务。目前,许多流行的应用程序都使用TCP。 连接:正式发送数据之前,提前建立好一种虚拟的&…

String常用操作

String常用方法 构造字符串 常用的构造字符串有3种: 1.直接赋值String s "abcd"; 2.实例化调用构造方法String s new String("abcd"); 3.实例化传字符数组 char[] ch {a,b,c,d}; String s new String(ch);字符串比较 比较 比较的是两个…

40.8K开源交流社区平台:Discourse

Discourse: 开放源代码,打造社区讨论的自由家园- 精选真开源,释放新价值。 概览 Discourse是一个完全开源的社区平台,为那些希望完全控制自己网站运行方式和地点的组织和个人提供服务。经过十多年的实战考验,Discours…

索引 ---- mysql

目录 1. 索引 1.1 概念 1.2 作用 1.3 使用场景 1.4 使用 1.4.1查看索引 1.4.2 创建索引 1.4.3 删除索引 1.5 注意事项 1.6 索引底层的数据结构 (面试经典问题) 1. 索引 1.1 概念 索引是一种特殊的文件,包含着对数据表里所有记录的引用指针。可以对表中的…

数据库(18)——DCL权限控制

MySQL常用权限 权限说明ALL,ALL PRIVILEGES所有权限SELECT查询数据INSERT插入数据UPDATE修改数据DELETE删除数据ALTER修改表DROP删除数据库/表/视图CREATE创建数据库/表 DCL语法 查询权限 SHOW GRANTS FOR 用户名主机名; 查询hello的权限 SHOW GRANTS FOR hellolocalhost; 授…

方法重写

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 基类的成员都会被派生类继承,当基类中的某个方法不完全适用于派生类时,就需要在派生类中重写父类的这个方法,这和…

pycharm链接auto al服务器

研0提前进组,最近阻力需求是把一个大模型复现,笔者电脑18年老机子,无法满足相应的需求。因此租用auto dl服务器。本文记录自己使用pycharm(专业版)链接auto dl期间踩过的坑。 1.下载pycharm专业版 这一步不解释了&am…

ESP32 WSL环境搭建

克隆代码 代码链接:https://gitee.com/EspressifSystems/esp-idf 克隆代码: git clone https://gitee.com/EspressifSystems/esp-idf 安装环境 cd esp32 /usr/bin/python3 ./esp-idf/tools/idf_tools.py 这里可能需要安装比较久, 有些需要…

基于51单片机的俄罗斯方块

一.硬件方案 本设计采用STC89C52RC单片机作为系统的芯片,实现人机交互、娱乐等功能。选用LCD12864实现俄罗斯方块游戏界面、图形显示;选用独立按键实现游戏控制。本设计实现的基本功能是:用按键控制目标方块的变换与移动;消除一行…

Java 多线程创建:三种主要方法

多线程编程是Java中一个重要的技术点,它允许程序并行执行多个任务,从而提高程序的执行效率。本文将详细介绍在Java中创建多线程的三种主要方法:继承Thread类、实现Runnable接口以及使用Callable和Future接口。 1. 继承 Thread 类 继承Threa…

python_将二维列表转换成HTML格式_邮件相关

python_将二维列表转换成HTML_邮件相关 data[["理想","2"],["理想2","3"]]def list_to_html_table(data):"""将二维列表转换为HTML表格格式的字符串。参数:data -- 二维列表,表示表格的数据。返回:一个字符…

最新国内AI工具(ChatGPT4.0、GPTs、AI绘画、文档分析使用教程)

如何利用AI提高内容生产效率? AI(人工智能)正以惊人的速度改变我们的生活方式,尤其是在内容生产领域。作为一名创作者,你可能会发现自己在面对海量信息时无从下手,或者在紧迫的截止日期前感觉力不从心。这时…

汽车数据应用构想(二)

一直说数据价值场景,啥叫有价值?啥样的场景有价值?按互联网的价值观来看,用户的高频需求就是价值。用户也许不会付费,但只要他天天用,那就是流量,就是用户黏性,就是价值!…

【Qt】对话框

文章目录 1 :peach:对话框介绍:peach:2 :peach:对话框的分类:peach:2.1 :apple:模态对话框:apple:2.2 :apple:非模态对话框:apple:2.3 :apple:混合属性对话框:apple: 3 :peach:Qt 内置对话框:peach:3.1 :apple:消息对话框 QMessageBox:apple: 1 🍑对话框介绍&#x…

【一刷《剑指Offer》】面试题 29:数组中出现次数超过一半的数字

力扣对应题目链接:169. 多数元素 - 力扣(LeetCode) 牛客对应题目链接:数组中出现次数超过一半的数字_牛客题霸_牛客网 (nowcoder.com) 核心考点 : 数组使用,简单算法的设计。 一、《剑指Offer》对应内容 二…

计算机网络学习实践:模拟PPP协议验证虚拟局域网(VLAN)

计算机网络实践:模拟PPP协议&&验证虚拟局域网(VLAN) 挺有意思的大家可以跟着做一做,我是跟着韩志刚老师的视频做的 https://www.bilibili.com/video/BV1Qr4y1N7cH?p31&vd_source7831c5b97cfc5c745eb48ff04f6515e7 …