链表的带环问题 链表的深度拷贝

news2024/11/19 23:22:38

1.1. 链表是否带环

代码很简单,最主要就是如何证明

  1. 首先判断链表是否带环,可以定义两个指针,一个快指针一个慢指针。快指针走两步,慢指针走一步
  2. 一定会相遇吗?有没有可能会超过?
  3. 假设进环的时候fast和slow的相隔距离是N,每走一步就是N - 1 当到了0的时候就会追上
  4. 意思就是说:每次追击,距离都会-1,此时不管是偶数环还是奇数环都没关系
  5. 这里用动图来解释一下,在这个动图中C = 5

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. 问题2

  1. 此时用动图来说明问题,N分为偶数和奇数的情况,但是这个建立在slow走1步,fast走3步的情况
  2. fast走3步,意味着每次的追击距离 - 2
  3. 下面这个表示为偶数的情况

  1. 下面这个是奇数的情况,当追过头的时候C - 1 = 偶数,下一轮就能追上

  1. slow走1步,fast 走3步、4步、5步,可以吗?一定会追上吗?请证明
  2. 假如同时存在N为奇数,C为偶数那么就永远追不上了

  1. 满足条件的:
    1. 为偶数个时候就能追上
    2. N是奇数的时候第一轮会超过1个,第二趟C-1为偶数 就可以追上了
  2. 所以终究还是有机会追上的对吗?(doge)

1.3. 返回链表开始入环的第一个节点

  1. 推导过程,根据公式推导L = (X - 1)* C + C - N;
  2. x 表示: slow进环前走了多少圈 ,C 是一圈的距离。x最少走一圈,不然等式就不会成立

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow = head,*fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)//此时说明是带环链表并且相遇了
        {
           struct ListNode* meet = fast;
            while(head != meet)
            {
                head = head->next;
                meet = meet->next;
            }
            return meet;
        }
    }
    return NULL;
}

1.3.1方法2

  1. 在开始之前,先说一下相交链表,然后再实现第二个方法
  2. 相交链表,题目链接

  1. 思路
    1. 首先判断是否相交?遍历链表找到尾节点
    2. 再找尾节点的同时顺便记录两个链表的长度
    3. 两个链表相减,用abs();函数得出绝对值,让长链表走相减步数,使得和短链表长度一致
    4. 最后让两个链表不相等的表达式做出判断。假如相等跳出循环,得出的就是相交的起始节点
  1. 注意:修改代码时需要想到被影响的值也需要进行修改
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* curA = headA,*curB = headB;
    int lenA = 1,lenB = 1;//这里+1,因为遍历链表时,满足条件最后一个节点不进入
    while(curA->next)//这里是找未节点
    {
        curA = curA->next;
        lenA++;
    }
    while(curB->next)
    {
        curB = curB->next;
        lenB++;
    }
    if(curA != curB)
        return NULL;
    //假设法
    int Dval = abs(lenA - lenB);//求绝对值
    struct ListNode* LongList = headA,*ShortList = headB;//这个假设 headA是长链表
    if(lenB > lenA)//假设失败,headB更长,此时让 B成为长链表
    {
        LongList = headB;
        ShortList = headA;
    }
    while(Dval--)//走差值步
    {
        LongList = LongList->next;
    }
    while(LongList != ShortList)
    {
        LongList = LongList->next;
        ShortList = ShortList->next;
    }
    //走到这说明前面条件都满足了
    return LongList;
    //return headA;更改代码有很多地方可以都有牵连,这里就是,经量不要拿原指针,以防找不到最初指向
}
  1. 就是快慢指针相遇后,断开meet节点,创建一个新的节点,新的节点拿到meet->next,指向的下一个节点的地址就行。可以发现最后变成了相交链表的问题

  2. 调用上面的相交链表的函数,返回的节点就是,入环时的第一个节点

struct ListNode* ListIntersectNode(struct ListNode* listA,struct ListNode* listB)
{
    struct ListNode* curA = listA,*curB = listB;
    int lenA = 0,lenB = 0;
    while(curA)//找共同尾节点
    {
        curA = curA->next;
        lenA++;
    }
    while(curB)
    {
        curB = curB->next;
        lenB++;
    }
    int k = abs(lenA - lenB);
    //假设法
    struct ListNode* longlist = listA,*shortlist = listB;
    if(lenB > lenA)
    {
        longlist = listB;
        shortlist = listA;
    }
    //让长的链表走差值步
    while(k--)
    {
        longlist = longlist->next;
    }
    while(longlist != shortlist)
    {
        longlist = longlist->next;
        shortlist = shortlist->next;
    }
    return longlist;
}
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 = fast;
            struct ListNode* newnode = meet->next;
            meet->next = NULL;
            return ListIntersectNode(head,newnode);
        }
    }
    return NULL;
}

2. 链表的深度拷贝

  1. 题目的要求是说拷贝对应原节点的值,并且和原链表的指向表示相同的方向
  2. 注意:每个节点一定不可以指向原链表,要指向拷贝链表

  1. 第一步,建立联系
    1. 首先在原链表每个节点后面都插入一个节点,拷贝前一个值
    2. 然后和在这个两链表之间插入,与之和这个原链表建立联系
  2. 第二步,解决random指向
    1. random的指向是随机的所以我们可以通过前一个节点来访问对应指向,保存copy位置节点
    2. random的情况可能指向为NULL,正常有节点的情况 看到图中
    3. 可以得出copy->random = cur->random->next ;这样就指向了之前拷贝在节点后的 7 了,这样拷贝的链表就建立了联系
  3. 分离链表
    1. 这个通过尾插的方式来把原链表上复制的几点取下来,所以需要一个头节点和尾节点 为什么?因为题目要求最后要返回一个头节点,而我们要尾插节点 
    2. 看上面的图,这里创建两个 copy的节点和一个after节点,刚好有三个节点还可以顺便还原 原链表
    3.  只要尾插到后面就行了,链表尾插还不会的可以去看看之前的博客,这里详细的讲了如何尾插--> 单链表的文章

struct Node* copyRandomList(struct Node* head) {
    if(head == NULL)
    return NULL;
    struct Node* cur = head;
    while(cur)
    {
        struct Node* CopyNode = (struct Node*)malloc(sizeof(struct Node));
        CopyNode->val = cur->val;//拷贝值
        //链接前后节点
        CopyNode->next = cur->next;
        cur->next = CopyNode;
        
        cur = CopyNode->next;//跳到copy的后一个节点
    }
    //解决random的指向
    cur = head;
    while(cur)
    {
        struct Node* copy = cur->next;
        if(cur->random == NULL)//原链表指向NULL,拷贝的链表也指向NULL
        {
            copy->random = NULL;
        }
        else//本题中精华代码部分
        {
            copy->random = cur->random->next;
        }
        cur = copy->next;
    }
    //分离链表
    struct Node* copyhead = NULL,*copytail = NULL;
    cur = head;
    while(cur)
    {
        struct Node* copy = cur->next;//图中所标是第二次遍历时的位置
        struct Node* after = copy->next;

        if(copyhead == NULL)
        {
            copyhead = copytail = copy;
        }
        else//尾插
        {
            copytail->next = copy;
            copytail = copytail->next;//因为前面已经尾插了一个数据,所以到下一个位置
        }
        //链表的恢复
        cur->next = after;
        cur = after;
    }
    return copyhead;
}

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

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

相关文章

87、动态规划-最长地址子序列

思路: 使用递归来理解题目,然后在看如何优化,假设我当前使用元素那么最长是多少,如果不使用当前元素最长是多少,然后取最大值。 代码如下: //算出最长递增子序列的长度public static int lengthOfLIS02(…

【机器学习】集成方法---Boosting之AdaBoost

一、Boosting的介绍 1.1 集成学习的概念 1.1.1集成学习的定义 集成学习是一种通过组合多个学习器来完成学习任务的机器学习方法。它通过将多个单一模型(也称为“基学习器”或“弱学习器”)的输出结果进行集成,以获得比单一模型更好的泛化性…

批量美化图片,轻松实现多张图片描边,让图片瞬间焕发新生!

图片已成为我们日常生活中不可或缺的一部分。无论是社交媒体上的个人分享,还是商业宣传中的产品展示,高质量、精美的图片都扮演着至关重要的角色。然而,对于许多人来说,图片处理仍然是一个令人头疼的问题。现在,我们为…

激动,五四青年节,拿下YashanDB认证YCP

📢📢📢📣📣📣 作者:IT邦德 中国DBA联盟(ACDU)成员,10余年DBA工作经验, Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主,全网粉丝10万 擅长主流Oracle、My…

SpringTask定时任务

SpringBoot项目定时任务 首先在启动类引入注解EnableScheduling然后在方法中加注解Scheduled(cron“”)cron表达式 生成cron https://www.pppet.net/

牛客热题:链表中的倒数最后K个节点

📟作者主页:慢热的陕西人 🌴专栏链接:力扣刷题日记 📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言 文章目录 牛客热题:链表中的倒数最后K个节点题目链…

PHP医疗不良事件上报系统源码 AEMS开发工具vscode+ laravel8 医院安全(不良)事件报告系统源码 可提供演示

PHP医疗不良事件上报系统源码 AEMS开发工具vscode laravel8 医院安全(不良)事件报告系统源码 可提供演示 医院安全不良事件报告系统(AEMS);分为外部报告系统和内部报告系统两类。内部报告系统主要以个人为报告单位&…

《苍穹外卖》Day12部分知识点记录——数据统计-Excel报表

一、工作台 需求分析和设计 接口设计 今日数据接口订单管理接口菜品总览接口套餐总览接口订单搜索(已完成)各个状态的订单数量统计(已完成) 代码实现 今日数据接口 1. WorkspaceController 注意不要导错包了 package com.sk…

【c++】继承学习(二):探索 C++ 中派生类的默认机制与静态成员共享

🔥个人主页:Quitecoder 🔥专栏:c笔记仓 目录 1.派生类的默认成员函数2.继承与友元3.继承与静态成员 朋友们大家好,本篇文章我们来学习继承的第二部分 1.派生类的默认成员函数 来看下面的类: class Person…

K8S执行完毕kubectl init xxx 执行 kubectl get ns 报错才connect: connection refused

问题场景: 在安装完毕K8S之后,执行 kubectl get ns 报错: [rootmaster ~]# kubectl get pods E0501 08:34:55.770030 11268 memcache.go:265] couldnt get current server API group list: Get "https://192.168.1.100:6443/api?ti…

深度学习项目实战:Python深度强化学习求解动态旅行商问题

深度强化学习(Deep Reinforcement Learning,DRL)可以用于解决优化问题,尤其是具有复杂、高维度的状态空间和动作空间的问题。它结合了深度学习的强大表示能力和强化学习的学习框架,深度神经网络可以学习复杂的特征和模式&#xff…

【深耕 Python】Quantum Computing 量子计算机(1)图像绘制基础

一、绘制静止图像 使用matplotlib库绘制函数图像y sin(pi * x): import math import matplotlib.pyplot as pltx_min -2.0 x_max 2.0N 1000x1 [] y1 []for i in range(N 1):x x_min (x_max - x_min) * i / Ny math.sin(math.pi * x)x1.append(x)y1.append(y)plt.xl…

基于51单片机的交通灯设计—可调时间、夜间模式

基于51单片机的交通灯设计 (仿真+程序+原理图+设计报告) 功能介绍 具体功能: 1.四方向数码管同时显示时间; 2.LED作红、绿、黄灯 3.三个按键可以调整红绿灯时间; 4.夜间模式&am…

循环神经网络模块介绍(Pytorch 12)

到目前为止,我们遇到过两种类型的数据:表格数据和图像数据。对于图像数据,我们设计了专门的卷积神经网络架构(cnn)来为这类特殊的数据结构建模。换句话说,如果我们拥有一张图像,我们 需要有效地利用其像素位置&#xf…

85、动态规划-零钱兑换

思路: 还是老样子,还是先使用递归方式来解,然后通过递归推动态规划。那递归如何设计? 定义一个递归方法:表示从index开始到N达到剩下的值(目标值减去上一步的值)做少可以得到数量是多少。int process(in…

快速幂笔记

快速幂即为快速求出一个数的幂&#xff0c;这样可以避免TLE&#xff08;超时&#xff09;的错误。 传送门&#xff1a;快速幂模板 前置知识&#xff1a; 1) 又 2) 代码&#xff1a; #include <bits/stdc.h> using namespace std; int quickPower(int a, int b) {int…

STM32单片机wifi云平台+温度+烟雾+火焰+短信+蜂鸣器 源程序原理图

目录 1. 整体设计 2. 液晶显示 3. Ds18b20温度传感器 4. Mq2烟雾传感器 5. 火焰传感器传感器 6. 蜂鸣器驱动控制 7. 按键 8. Gsm短信模块 9. Esp8266wifi模块 10、源代码 11、资料内容 资料下载地址&#xff1a;STM32单片机wi…

PR2019新建项目教程

一&#xff0c;新建项目&#xff1a; 设置工程名称&#xff0c;选择工程目录位置&#xff0c;其他默认&#xff1a; 二&#xff0c;新建序列 新建项->序列&#xff1a; 设置序列参数&#xff1a; 三&#xff0c;导出设置 设置导出参数&#xff1a;

vivado Aurora 8B/10B IP核(11)- 共享逻辑(Share Logic)

Vivado IDE 中的共享逻辑选项配置核心&#xff0c;包括可收集的资源&#xff0c;如收发器四路 PLL&#xff08;QPLL&#xff09;&#xff0c;收 发器差分缓冲区&#xff08;IBUFDS_GTE2&#xff09;以及核心或示例设计中的时钟和复位逻辑。 当选择了核心选项中的包含共享逻辑时…

QT防止自研软件被复制的基本操作(二)

参考一 自研软件为了防止被人任意复制传播&#xff0c;需要设置注册使用模式。基本原理&#xff1a;通过计算机的特异性编号&#xff0c;加上自己的编码&#xff0c;使用加密算法算出一个生成码。 一、计算机的特异性编号 硬盘的编号&#xff1a;最后一块硬盘的编号就行&#…