【数据结构练习】单链表OJ题(二)

目录

    • 一、相交链表
    • 二、环形链表1
    • 三、环形链表2
    • 四、链表分割
    • 五、复制带随机指针的链表

一、相交链表

题目:
在这里插入图片描述
示例:
在这里插入图片描述
注意:不能根据节点的值来比较是否相交,而是根据节点在内存中是否指向相同的位置。

例如以上图:

链表A:4、1、8、4、5
链表B:5、6、1、8、4、5

链表A和链表B都有节点的值为1,但是它们在内存中指向不同的位置,而值为8的节点(A的第三个节点、B的第四个节点)则在内存中指向相同的位置。

大体思路:链表A和链表B如果相交,那么它们的后几个或者一个节点的位置是一样的。它们的长度不一定一样长,所以要先计算出链表A和链表B的长度,让较长的链表先走长度差的距离,然后再同时走,直到两个链表相交,返回那个开始相交的节点。

计算链表长度:
分别定义一个变量遍历链表,不为空计数器加1往后走,直到循环结束跳出。

如果没有相交返回空:
此时分别遍历两个链表的指针已经走到尾了。假设两个链表有相交,不管是一个还是多个,那么这两个指针肯定是一样的(指相交);如果两个链表不相交,即使是最后的节点也同样不相交,所以就直接返回NULL。

较长的链表先走节点数之差:
用一个变量等于链表长度之差,因为并不知道是A链表长还是B链表长,所以使用绝对值函数abs。先假设是A链表长,B链表短。设置一个条件,如果A链表的长度小于B链表的长度,就反过来。如果假设的是对的,较长的链表先走长度之差的距离。

一起走,直到相交:
两个链表同时走到某个节点相交了,就跳出循环,然后赋给新的头指针,并且返回这个头指针。

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    int len1=0;
    int len2=0;
    struct ListNode *cur1=headA;
    struct ListNode *cur2=headB;
    //先算出两个链表的节点数
    while(cur1)
    {
        len1++;
        cur1=cur1->next;
    }
    while(cur2)
    {
        len2++;
        cur2=cur2->next;
    }
    //如果没有相加点返回空
    if(cur1!=cur2)
    {
        return NULL;
    }
    //长的先走节点数之差
    int k=abs(len1-len2);
    struct ListNode *longhead=headA;
    struct ListNode *shorthead=headB;
    if(len1<len2)
    {
        longhead=headB;
        shorthead=headA;
    }
    while(k)
    {
        longhead=longhead->next;
        k--;
    }
    //一起走,直到相交
    while(longhead!=shorthead)
    {
        longhead=longhead->next;
        shorthead=shorthead->next;
    }
    struct ListNode *newhead=longhead;
    return newhead;
}

二、环形链表1

题目:
在这里插入图片描述
使用的是双指针法

定义两个快慢指针fast和slow,从起点出发。先fast一次走两步,slow一次走一步,再判断两个指针是否相同。如果链表有环,那么fast或者fast->next永远不为空,一直在循环里转;如果链表没有环,fast又走得比slow快,所以fast或者fast->next到空就结束。这里fast比slow快一步,即每次距离缩短1步,所以只要有环,fast先在环里转,然后slow随后进环,每次它们的距离缩短1,最终相遇。

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

三、环形链表2

题目:
在这里插入图片描述
这题与上面的题多增加了一个设定,如果有环,返回的是开始入环的第一个节点。没有环返回空。

那么始入环的第一个节点怎么找呢?其实这里是有关数学计算的。

还是使用两个快慢指针,fast一次走两步,slow一次走一步。如果链表没有环,fast或者fast->next为空,然后返回空指针。如果链表有环,在环的某个位置相遇(注意:fast先入环,可能已经在环里转了n圈了)。假设相遇点与入环点的距离为x,起始点到入环点的距离为L,环的剩下的距离为nC-x(n表示fast已经在环里转了n圈)。

fast从起始点到相遇点的距离为:L+nC+x
slow从起始点到相遇点的距离为:L+x

因为fast一次的步长为slow的两倍

所以:
2(L+x)=L+nC+x
L+x=nC
L=nC-x

根据数学计算转换为:两个指针分别一个从相遇点走,另一个从起始点走,以相同的步长,它们会在入环的第一个节点相遇,然后返回这个相遇点就是入环的第一个节点。

在这里插入图片描述

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *slow=head;
    struct ListNode *fast=head;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
        if(slow==fast)
        {
            struct ListNode *rid=slow;
            struct ListNode *mrid=head;
            while(rid!=mrid)
            {
                rid=rid->next;
                mrid=mrid->next;
            }
            return rid;
        }
    }
    return NULL;
}

四、链表分割

题目:
在这里插入图片描述
假设一个链表为:

2、3、1、4、2、5

假设x等于3,数据小于的3的节点有:2、1、2
大于等于3的节点有:3、4、5

题目要求将小于x的节点排在其余节点之前,并且不能改变原来的数据顺序。

大体思路:这里我们可以采用分为两个链表的方式。将数据小于x的节点放在一个A链表里,大于等于的放在B链表里。然后把A链表的尾节点与B链表的头节点连接起来。

但是这里要注意一个问题,如果A链表没有节点怎么与B链表连接,这种情况要单独判断,比较麻烦。所以我们可以采用创建哨兵位头节点解决这个问题。

注意:最后B链表的尾节点要指向空指针,同时释放两个哨兵位节点,返回第一个有效节点。
在这里插入图片描述

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        struct ListNode* cur=pHead;
        struct ListNode* head1, *tail1, *head2, *tail2;
        head1=tail1=(struct ListNode*)malloc(sizeof(struct ListNode));
        head2=tail2=(struct ListNode*)malloc(sizeof(struct ListNode));
        while(cur)
        {
            if(cur->val<x)
            {
                tail1->next=cur;
                tail1=tail1->next;
            }
            else
            {
                tail2->next=cur;
                tail2=tail2->next;
            }
            cur=cur->next;
        }
        tail2->next=NULL;
        tail1->next=head2->next;
        struct ListNode* newhead=head1->next;
        free(head1);
        free(head2);
        return newhead;
    }
};

五、复制带随机指针的链表

题目:
在这里插入图片描述
要拷贝原节点,并且拷贝节点的随机指针也要指向对应的节点,最后还原原链表。

每个拷贝节点都在原节点的后面:
用一个变量cur遍历链表,只要不为空每次进入循环先定义一个变量next记录原链表的下一个位置(cur在原链表的下一个位置,方便一次拷贝完找到),然后开辟一块空间copy为拷贝节点,把原节点的值赋给拷贝节点,再让cur的下一个地址为copy,copy连接next。
在这里插入图片描述
置每个拷贝random:
原链表的每个节点的random指针都有对应的指向,拷贝节点也要有random指针对应的指向,只是拷贝节点的random指针要与原节点的random指针指向保持一致。比如原节点13的random指针指向原节点的7,那么拷贝节点13的random指针则指向拷贝节点的7。

问题是拷贝节点的random指针怎么找到它对应的节点呢?其实上面一步把每个拷贝节点放在原节点的后面有一个好处,方便这一步找到它的random指针。

以拷贝节点13为例:拷贝节点13的random指针,就是它的原节点的random指针所指向的原节点的下一个节点。如果原节点的random指向空,拷贝节点的randon也是指向空。

在这里插入图片描述
拷贝节点解下来,尾插在一起,恢复原链表:
在这里插入图片描述

struct Node* copyRandomList(struct Node* head) {
	struct Node* cur=head;
    //复制某个节点到原节点的后面
    while(cur)
    {
        struct Node* next=cur->next;
        struct Node* copy=(struct Node*)malloc(sizeof(struct Node));
        copy->val=cur->val;
        cur->next=copy;
        copy->next=next;
        cur=next;
    }
    cur=head;
    //处理拷贝节点的random指针指向
    while(cur)
    {
        struct Node* copy=cur->next;
        if(cur->random==NULL)
        {
            copy->random=NULL;
        }
        else
        {
            copy->random=cur->random->next;
        }
        cur=copy->next;
    }
    cur=head;
    //把拷贝指针连接起来,还原原来的链表
    struct Node* newhead=NULL;
    struct Node* tail=NULL;
    while(cur)
    {
        struct Node* copy=cur->next;
        struct Node* next=copy->next;
        if(tail==NULL)
        {
            newhead=tail=copy;
        }
        else
        {
            tail->next=copy;
            tail=tail->next;
        }
        cur->next=next;
        cur=next;
    }
    return newhead;
}

在这里插入图片描述
感谢观看~

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

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

相关文章

无涯教程-Android - RadioButton函数

RadioButton有两种状态:选中或未选中,这允许用户从一组中选择一个选项。 Radio Button 示例 本示例将带您完成一些简单的步骤,以展示如何使用Linear Layout和RadioButton创建自己的Android应用程序。 以下是修改后的主要Activity文件 src/MainActivity.java 的内容。 packa…

数学建模:数据的预处理

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 文章目录 数据预处理数据变换数据清洗缺失值处理异常值处理 数据预处理 数据变换 常见的数据变换的方式&#xff1a;通过某些简单的函数进行数据变换。 x ′ x 2 x ′ x x ′ log ⁡ ( x ) ∇ f ( x k )…

MATLAB中mod函数转化为C语言

背景 有项目算法使用matlab中mod函数进行运算&#xff0c;这里需要将转化为C语言&#xff0c;从而模拟算法运行&#xff0c;将算法移植到qt。 MATLAB中mod简单介绍 语法 b mod(a,m) 说明 b mod(a,m) 返回 a 除以 m 后的余数&#xff0c;其中 a 是被除数&#xff0c;m 是…

储能辅助电力系统调峰的容量需求研究(matlab代码)

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序参考文献《储能辅助电力系统调峰的容量需求研究》&#xff0c;是一个很常规很经典的matlab优化代码&#xff0c;主要是对火电、风电和储能等电力设备主体进行优化调度&#xff0c;在调峰能力达不到时采…

Java 面试 - Redis

Redis Redis 是基于键值对的非关系型数据库。Redis 拥有string、hash、list、set、zset等多种数据结构, redis具有惊人的读写性能, 其优秀的持久化机制是的它在断电和机械故障时也不会发生数据丢失, 可以用于热点数据存放, 还提供了键过期、发布订阅、食物、流水线、LUA脚本等多…

创建聊天机器人:产品专属ChatGPT智能问答机器人,可添加任意网站

ChatGPT智能问答机器人可以广泛应用于各种SaaS产品&#xff0c;通过创建聊天机器人可以快速反馈用户&#xff0c;并且针对性的提供解决方案&#xff0c;非常高效的完成客户问答反馈。 聊天机器人是生活中常见的一种交互方式&#xff0c;机器人根据用户输入的关键字&#xff0c;…

ChatGPT~Error1015You are being rate limited

目录 问题背景 问题的原因 下来说说解决方案 总结 问题背景 今天使用Chatgpt的时候突然出现"You are being rate limited"的错误提示。 问题的原因 小问题了&#xff0c;又不是第一次被弄出来了&#xff0c;莫慌。 让我们先看看Chatgpt自己是怎么解释这个问题…

JVM类加载器

一、类与类加载器 类加载器虽然只用于实现类的加载动作&#xff0c;但它在Java程序中起到的作用却远超类加载阶段。对于 任意一个类&#xff0c;都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性&#xff0c;每一个类加载器&#xff0c;都拥有一个独…

win | wireshark | 在win上跑lua脚本 解析数据包

前提说明&#xff1a;之前是在linux 系统上配置的&#xff0c;然后现在 在配置lua 脚本 &#xff0c;然后 分析指定协议 的 数据包 其实流程也比较简单&#xff0c;但 逻辑需要缕清来 首先要把你 预先准备的 xxx.lua 文件放到wireshark 的安装文件中&#xff0c;&#xff08;我…

并发编程的故事——共享模式之无锁

共享模式之无锁 文章目录 共享模式之无锁一、提出问题二、CAS和volatile三、原子整数四、原子引用五、原子数组六、原子更新器七、原子累加器八、unsafe 一、提出问题 关于对共享变量修改的多线程问题其实就是指令交错问题导致取值的时机相同&#xff0c;最后修改之后以最后一…

ChatGPT 制作可视化柱形图突出显示第1名与最后1名

对比分析柱形图的用法。在图表中显示最大值与最小值。 像这样的动态图表的展示只需要给ChatGPT,AIGC,OpenAI 发送一个指令就可以了, 人工智能会快速的写出HTML与JS代码来实现。 请使用HTML,JS,Echarts完成一个对比分析柱形图,在图表中突出显示第1名和最后1名用单独一种不…

数据结构--树4.2.2(二叉树--遍历)

目录 一、二叉树的建立 二、二叉树的遍历算法 一、二叉树的建立 CreateBitree(Bitree *t){char c;scanf("%c",&c);if( c){*t NULL;}else{*t(Bitnode*)malloc(sizeof(Bitnode));(*t)->data c;CreateBitree(&(*t)->lchild);CreateBitree(&(*t)-&…

C++ 手写实现类似lower_bound和upper_bound的二分功能

目录 lower_bound和upper_bound介绍手动实现类似的二分效果lower_boundupper_bound另一种常见的二分形式 对lower_bound函数使用lamda函数 lower_bound和upper_bound介绍 lower_bound函数的作用是查找范围内第一个大于等于目标元素的元素迭代器/指针 数组的简单使用&#xff…

vscode 与 C++

序 具体流程的话&#xff0c;官方文档里都有的&#xff1a;C programming with Visual Studio Code 浏览器下载一个mingw64&#xff0c;解压&#xff0c;配置环境变量vscode里安装c相关的插件没了 第一步只看文字&#xff0c;可能有点抽象&#xff0c;相关视频&#xff1a; …

科技云报道:AI+云计算共生共长,能否解锁下一个高增长空间?

科技云报道原创。 在过去近一年的时间里&#xff0c;AI大模型从最初的框架构建&#xff0c;逐步走到落地阶段。 然而&#xff0c;随着AI大模型深入到千行百业中&#xff0c;市场开始意识到通用大模型虽然功能强大&#xff0c;但似乎并不能完全满足不同企业的个性化需求。 大…

Three.js后处理后物体表面出现条纹

初始化 WebGLRenderer 时简单启用 logarithmicDepthBuffer: true 解决了问题。 根据文档&#xff0c;启用可能会导致性能下降&#xff0c;因此请根据您的性能预算考虑使用它。 缩小相机的near和far 后处理对于深度精度非常敏感。大视锥体很快就会使此类 AO 通道变得无法使用 th…

因果推断(六)基于微软框架dowhy的因果推断

因果推断&#xff08;六&#xff09;基于微软框架dowhy的因果推断 DoWhy 基于因果推断的两大框架构建&#xff1a;「图模型」与「潜在结果模型」。具体来说&#xff0c;其使用基于图的准则与 do-积分来对假设进行建模并识别出非参数化的因果效应&#xff1b;而在估计阶段则主要…

合宙Air724UG LuatOS-Air LVGL API控件--容器 (Container)

容器 (Container) 容器是 lvgl 相当重要的一个控件了&#xff0c;可以设置布局&#xff0c;容器的大小也会自动进行调整&#xff0c;利用容器可以创建出自适应成都很高的界面布局。 代码示例 – 创建容器 cont lvgl.cont_create(lvgl.scr_act(), nil) lvgl.obj_set_auto_re…

java之SpringBoot基础、前后端项目、MyBatisPlus、MySQL、vue、elementUi

文章目录 前言JC-1.快速上手SpringBootJC-1-1.SpringBoot入门程序制作&#xff08;一&#xff09;JC-1-2.SpringBoot入门程序制作&#xff08;二&#xff09;JC-1-3.SpringBoot入门程序制作&#xff08;三&#xff09;JC-1-4.SpringBoot入门程序制作&#xff08;四&#xff09;…

C语言练习7(巩固提升)

C语言练习7 编程题 前言 “芳林新叶催陈叶&#xff0c;流水前波让后波。”改革开放40年来&#xff0c;我们以敢闯敢干的勇气和自我革新的担当&#xff0c;闯出了一条新路、好路&#xff0c;实现了从“赶上时代”到“引领时代”的伟大跨越。今天&#xff0c;我们要不忘初心、牢记…