力扣OJ题——随机链表的复制

题目:

138. 随机链表的复制

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

要求:构造这个链表的 深拷贝

 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。

注意:复制链表中的指针都不应指向原链表中的节点 

示例:

思路一:暴力求解

1.拷贝链表

2.处理拷贝链表每个节点的random指针,这里封装一个函数来实现~

通过分析,思路一的时间复杂度应该是O(N^2),这是一种很直观的思路,下面我们来实现

1.拷贝链表,代码如下:


    
    struct Node *copy = (struct Node*)malloc(sizeof(struct Node)),*p;
    p = copy;

    struct Node *cur = head;//cur指向原链表的头节点
    while(cur!=NULL)
    {
        struct Node *curcopy=(struct Node*)malloc(sizeof(struct Node));
        curcopy->val=cur->val;
        curcopy->random=NULL;
        copy->next=curcopy;//copy是curcopy的前驱,起着拷贝链表的连接作用
        
        copy = copy->next;//copy往后走
        cur=cur->next;//cur往后走
    }

    copy->next=NULL;//当cur = NULL后的处理

2.处理拷贝链表每个节点的random指针,代码如下:


    cur=head;//让在第一步使用过的cur回到原链表的头
    copy=p->next;//让在第一步使用过的copy来到拷贝链表的头

    while(cur!=NULL)
    {
        if(cur->random==NULL)
        {
            copy->random=NULL;
        }

        else
        {
            //find函数是用来找到random指向的节点
            copy->random=find(head,p->next,cur->random);
        }
       copy=copy->next;
       cur=cur->next;
    }

find函数的实现代码如下:


//find函数找到random指向的节点
 //head为原链表的头,copy为拷贝链表的头,dest为目标
struct Node* find(struct Node* head,struct Node* copy,struct Node* dest)
{
  
    while(head!=dest)
    {
        head=head->next;
        copy=copy->next;
    }

    return copy;
}

下面我们将第一步与第二步的代码整合起来,就形成完整的代码

struct Node* find(struct Node* head,struct Node* copy,struct Node* dest);
struct Node* copyRandomList(struct Node* head)
 {
	if(head == NULL) return NULL;

    struct Node *copy = (struct Node*)malloc(sizeof(struct Node)),*p;
    p = copy;

    struct Node *cur = head;
    while(cur!=NULL)
    {
        struct Node *curcopy=(struct Node*)malloc(sizeof(struct Node));
        curcopy->val=cur->val;
        curcopy->random=NULL;
        copy->next=curcopy;
        
        copy = copy->next;
        cur=cur->next;
    }

    copy->next=NULL;



    cur=head;
    copy=p->next;
    while(cur!=NULL)
    {
        if(cur->random==NULL)
        {
            copy->random=NULL;
        }

        else
        {
            copy->random=find(head,p->next,cur->random);
        }
       copy=copy->next;
       cur=cur->next;
    }

    return p->next;//返回拷贝链表的头
}

//find函数找到random指向的节点
struct Node* find(struct Node* head,struct Node* copy,struct Node* dest)
{
  
    while(head!=dest)
    {
        head=head->next;
        copy=copy->next;
    }

    return copy;
}

虽然时间复杂度为O(N^2),但力扣居然给过了

不过这里要说的是,这样暴力的思路往往不是面试官心中的答案,所以下面我们来看一下更优的思路二

思路二:

1.插入拷贝节点在原节点的后面

2.控制拷贝节点的random

   copy -> random = cur ->random->next(这句是精华)

3.解下拷贝节点,同时恢复原链表的指向

思路二的巧妙就在于它在建立拷贝链表时就和原链表有了联系,有了这种联系,之后处理random指针就能更高效,直接就能找到、、

我们看一下思路二的时间复杂度,已经变成了O(N),这就非常的nice

下面我们就来实现这个思路

第一步:插入拷贝节点在原节点的后面,代码如下

    struct Node* cur = head;//cur在原节点的头

    while (cur != NULL)//第一步
     {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;
        
        //插入
        copy->next = cur->next;
        cur->next = copy;

        //继续往下
        cur = copy->next;
    }

第二步:控制拷贝节点的random,代码如下

    cur = head;//恢复cur到原节点的头

    while (cur != NULL) //第二步
    {
       struct Node* curcopy =  cur->next;//拷贝节点

       if (cur->random == NULL)  
           curcopy->random = NULL;
       
       else
           curcopy->random =cur->random->next;
        
       cur = curcopy->next;
    }

第三步:解下拷贝节点使其成为独立的链表,同时恢复原链表的指向,代码如下

     cur = head;//恢复cur到原节点的头
     //第三步
    struct Node* copyhead=NULL,*copytail=NULL;//定义拷贝链表的头和尾

    while (cur != NULL)
    {
       struct Node* curcopy = cur->next;
       struct Node* curnext = curcopy->next;// curnext是原链表cur的下一个节点
       
      //通过尾插使拷贝节点成为独立的链表
      if(copyhead == NULL)//处理头
          copyhead = copytail = curcopy;

      else //处理其他情况
         {
             copytail->next = curcopy;
             copytail = copytail->next;
         }

      cur->next= curnext;//恢复原链表的指向

      cur = curnext;//cur往后走

    }

下面我们将这三步的代码整合起来,就形成完整的代码

struct Node* copyRandomList(struct Node* head)
 {
    if  (head == NULL)   return NULL;
      
    struct Node* cur = head;//cur在原节点的头
    while (cur != NULL)//第一步
     {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;

        //插入
        copy->next = cur->next;
        cur->next = copy;

        //继续往下
        cur = copy->next;
    }

    cur = head;//恢复cur到原节点的头
    while (cur != NULL) //第二步
    {
       struct Node* curcopy =  cur->next;//拷贝节点

       if (cur->random == NULL)  
           curcopy->random = NULL;
       
       else
           curcopy->random =cur->random->next;
        
       cur = curcopy->next;
    
    }


     cur = head;
     //第三步
    struct Node* copyhead=NULL,*copytail=NULL;

    while (cur != NULL)
    {
      struct Node* curcopy = cur->next;
      struct Node* curnext = curcopy->next;// curnext是原链表cur的下一个节点
       
      if(copyhead == NULL)
          copyhead = copytail = curcopy;

      else 
         {
             copytail->next = curcopy;
             copytail = copytail->next;
         }

      cur->next= curnext;//恢复原链表

      cur = curnext;
    }

    return copyhead;//返回拷贝链表的头
}

代码走起来

就过了

好啦,到此为止,今天的随机链表的复制问题就结束啦,如果文中分析,题解代码有不足的地方欢迎大家在评论区讨论和指正

让我们在接下来的时间里一起学习,一起进步吧~

c0fe1378f4b1464abb37998a472b5961.jpg

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

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

相关文章

websocket与Socket的区别

概念讲解 网络:通俗意义上,也就是连接两台计算器 五层网络模型:应用层、传输层、网络层、数据链路层、物理层 应用层 (application layer):直接为应用进程提供服务。应用层协议定义的是应用进程间通讯和交互的规则,不…

数据库事物复习

事务 比如说将张三的银行账户拿出一千给李四,首先需要查询张三的账户余额,扣除1000,然后如果给李四加上1000的过程中出现异常会回滚事务,临时修改的数据会回复回去。 -- 1. 查询张三账户余额 select * from account where name …

【2024软件测试面试必会技能】Selenium(6):元素定位_xpath定位

XPATH是什么 XPATH是一门在XML文档中查找信息的语言,XPATH可用来在XML文档中对元素和属性进行遍历,主流的浏览器都支持XPATH,因为HTML页面在DOM中表示为XHTML文档。Selenium WebDriver支持使用XPATH表达式来定位元素。 Xpath常用如下6种定位…

安卓APP和小程序渗透测试技巧总结

本文章仅供学习和研究使用,严禁使用该文章内容对互联网其他应用进行非法操作,若将其用于非法目的,所造成的后果由您自行承担。 由于安卓7开始对系统安全性做了些改动,导致应用程序不再信任客户端证书,除非应用程序明确…

OpenTiny Vue 组件库适配微前端可能遇到的4个问题

本文由体验技术团队 TinyVue 项目成员岑灌铭同学创作。 前言 微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略,每个应用可以选择不同的技术栈,独立开发、独立部署。 TinyVue组件库的跨技术栈能力与微前端十…

搜维尔科技:【周刊】适用于虚拟现实VR中的OptiTrack

适用于 VR 的 OptiTrack 我们通过优化对虚拟现实跟踪最重要的性能指标,打造世界上最准确、最易于使用的广域 VR 跟踪器。其结果是为任何头戴式显示器 (HMD) 或洞穴自动沉浸式环境提供超低延迟、极其流畅的跟踪。 OptiTrack 主动式 OptiTrack 世界领先的跟踪精度和…

医药之链:基于Django的智能药品管理系统

框架 Python 3.7 django 3.2.13 Bootstrap(前端) sqlite(数据库)导包 pip install django3.2.13 pip install pandas pip install xlwt环境搭建 登录 zfx 123456

docker 容器内服务随容器自动启动

docker 容器内服务随容器自动启动 背景准备工作方案一,直接修改.bashrc文件(简单粗暴)方案二,编写启动脚本加入.bashrc文件(文明一点)制作nginx服务自启动镜像测试新镜像,nginx服务随容器自动启…

HGAME week2 web

1.What the cow say? 测试发现可以反引号命令执行 ls /f* tac /f*/f* 2.myflask import pickle import base64 from flask import Flask, session, request, send_file from datetime import datetime from pytz import timezonecurrentDateAndTime datetime.now(timezone(…

【Java多线程】分析线程加锁导致的死锁问题以及解决方案

目录 1、线程加锁 2、死锁问题的三种经典场景 2.1、一个线程一把锁 2.2、两个线程两把锁 2.3、N个线程M把锁(哲学家就餐问题) 3、解决死锁问题 1、线程加锁 其中 locker 可以是任意对象,进入 synchronized 修饰的代码块, 相当于加锁&…

OpenGauss数据库本地搭建并结合内网穿透实现远程访问

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试 前言 openGauss是一款开源关系型数据库管理系统,采用木兰宽松许可证v2发行。openGauss内核深度融合…

【AIGC】开源音频工具AudioCraft

AudioCraft是一个开源框架,旨在生成高质量的音频,适用于音乐、声音生成和压缩等多种应用。 先听效果: aimusic 它由三个模型组成:MusicGen、AudioGen和EnCodec。 MusicGen: 这个模型使用了Meta拥有和特别许可的音乐进…

如何使用Docker本地部署Jupyter+Notebook容器并结合内网穿透实现远程访问

文章目录 1. 选择与拉取镜像2. 创建容器3. 访问Jupyter工作台4. 远程访问Jupyter工作台4.1 内网穿透工具安装4.2 创建远程连接公网地址4.3 使用固定二级子域名地址远程访问 本文主要介绍如何在Ubuntu系统中使用Docker本地部署Jupyter Notebook,并结合cpolar内网穿透…

UE4 C++联网RPC教程笔记(三)(第8~9集)完结

UE4 C联网RPC教程笔记(三)(第8~9集)完结 8. exe 后缀实现监听服务器9. C 实现监听服务器 8. exe 后缀实现监听服务器 前面我们通过蓝图节点实现了局域网连接的功能,实际上我们还可以给项目打包后生成的 .exe 文件创建…

edge安装fdm插件

下载 https://www.crxsoso.com/webstore/detail/ahmpjcflkgiildlgicmcieglgoilbfdp 安装 进入edge插件管理页面 edge://extensions/2. 将下载的crt文件拖到这个页面,就能自动安装了 在其他网页不能安装,会变成下载。

2024年noc比赛Coding创意编程赛项-创意实验室初赛模拟题

【单选题】 1.角色本来面向的方向是右方,执行下方积木后,角色面向的方向是() A.面向右上方 C.面向左上方 B.面向右下方 D.面向左下方 2.下列选项中关于图中按钮功能说法错误的是() A."本地传”按钮可以从本地电脑上传素材 B."重新画”按钮可以自己设计素材 C"…

QT的UI入门

二、UI入门 QWidget类(熟悉) QWidget类是所有组件和窗口的基类,内部包含了一些基础的界面特性。 常用属性: 修改坐标 x : const int 横坐标,每个图形的左上角为定位点,横轴的零点在屏幕的最左边&#xff0c…

Javase-方法的使用

文章目录 一 . 方法的初步认识二 . 方法的定义三 . 方法调用的执行过程四 . 实参与形参的关系五 . 方法的重载 一 . 方法的初步认识 方法其实就是一些代码片段,类似于c语言中的函数 方法存在的意义(理解): 是能够模块化的组织代码(当代码规模比较复杂的时候).做到代码被重复使…

一文搞懂match、match_phrase与match_phrase_prefix的检索过程

一、在开始之前,完成数据准备: # 创建映射 PUT /tehero_index {"settings": {"index": {"number_of_shards": 1,"number_of_replicas": 1}},"mappings": {"_doc": {"dynamic": …