python_ACM模式《剑指offer刷题》链表2

题目:

例如

面试tips:

询问有无时间复杂度或空间复杂度的限制。

思路:

本题的本质就是复杂链表的深拷贝

1. 暴力解法 → 第一次遍历原链表时构建一个复制了next的新链表,第二次遍历原链表,对每个原链表的节点的random从头寻找,同时同步在新链表寻找,即可找到复制链表每个节点的random。

时复O(N^2), 空复O(1)

2. 用空间换取时间,利用哈希表。在第一次遍历原链表时存储(原链表节点,新链表节点)的映射关系;则在第二次遍历原链表时为新链表根据哈希映射找到其next和random。

时复O(N), 空复O(N)

3. 拼接+拆分;时复O(N),空复O(1)

第一步:遍历原链表时对其进行复制,并将原链表(A,B,C,D...)与新链表(A',B',C',D',...)拼接成如下形式。

        

第二步:遍历上述链表的原节点(curr = curr.next.next),只要curr.random不为空,则curr.next(新链表对应节点)的random即为curr.random.next。说人话就是:上图中第一步构建完后就是上图,可以看到新链表节点(A',B',C',D',...)是没有random指针的(random指针为None), 这时可通过其前一个节点帮助找其复制后的random节点,例如

        

第三步:将其交错拆分,拆分时用next指针连接即可。

代码实现:

因为是acm模式,因此包括了二维数组转复杂链表,复杂链表转二维数组的过程,因此较长。核心代码模式从class solution开始,但很多大厂面试就是要自己从数组转链表,再从链表转数组进行打印,因此这里记录acm模式。

1. 思路一实现

class ListNode:
    def __init__(self, val , next = None, random = None):
        self.val = val
        self.next = next
        self.random = random

def arr2List(arr_2):
    # 二维数组构建复杂链表函数
    # 构建时将其存入哈希表
    my_dict = {}
    dummy_head = prev = ListNode(0)
    for i in range(len(arr_2)):
        my_dict[i] = prev.next = ListNode(arr_2[i][0])
        prev = prev.next
    # 至此dummy_head即为构建好的带有next的链表,但是没有random指针
    # 二维数组的第二个数字表示该节点random指针所指向的第几个节点
    prev = dummy_head.next
    for i in range(len(arr_2)):
        if arr_2[i][1] != None:
            prev.random = my_dict[arr_2[i][1]]
        else:
            prev.random = None
        prev = prev.next
    return dummy_head.next

def List2arr(head):
    # 复杂链表转二维数组
    arr_2 = []
    my_dict = {}
    curr = head
    i = 0
    while curr:
        tmp = [curr.val]
        arr_2.append(tmp)
        my_dict[curr] = i
        i += 1
        curr = curr.next
    curr = head
    while curr:
        # 添加random
        arr_2[my_dict[curr]].append(my_dict[curr.random] if curr.random else None)
        curr = curr.next
    return arr_2

class Solution:
    def copyRandomList(self, head):
        # 暴力方法 -- 先构建只含有next的单链表,再构建random的
        curr = head
        new_head = prev = ListNode(0)   # 虚拟指针 使得头节点操作与其他同
        while curr:
            new_curr = ListNode(curr.val)
            prev.next = new_curr
            prev, curr = new_curr, curr.next
        # 至此 复制了链表的第一层(即带next指针的)
        # 接下来 需要给复制链表上的每个节点添加random指针 原链表上从头开始走了多少步则复制链表上也从头开始走了多少步
        curr1 = head
        new_curr1 = new_head.next
        while curr1:
            curr2 = head
            new_curr2 = new_head.next
            # 找到curr1所对应的random,同步找到new_curr1所对应的random
            while curr2 and curr1.random != curr2:
                curr2 = curr2.next
                new_curr2 = new_curr2.next
            # 跳出来 此时即 curr2 == curr1.random,那么new_curr2所指向的位置即为new_curr1的random
            new_curr1.random = new_curr2
            curr1 = curr1.next
            new_curr1 = new_curr1.next
        return new_head.next

if __name__ == '__main__':
    # 复杂链表的构建,二维数组构建复杂链表,(用单链表用一维数组、双链表用二维数组构建的思想)
    arr_2 = [[7, None],[13, 0],[11, 4],[10, 2],[1, 0]]
    a = Solution()
    # 先将二维数组转为链表
    head = arr2List(arr_2)
    new_head = a.copyRandomList(head)
    # 再将链表转为二维数组
    new_arr2 = List2arr(new_head)
    print(new_arr2)

2. 思路二实现

class ListNode:
    def __init__(self, val , next = None, random = None):
        self.val = val
        self.next = next
        self.random = random

def arr2List(arr_2):
    # 二维数组构建复杂链表函数
    # 构建时将其存入哈希表
    my_dict = {}
    dummy_head = prev = ListNode(0)
    for i in range(len(arr_2)):
        my_dict[i] = prev.next = ListNode(arr_2[i][0])
        prev = prev.next
    # 至此dummy_head即为构建好的带有next的链表,但是没有random指针
    # 二维数组的第二个数字表示该节点random指针所指向的第几个节点
    prev = dummy_head.next
    for i in range(len(arr_2)):
        if arr_2[i][1] != None:
            prev.random = my_dict[arr_2[i][1]]
        else:
            prev.random = None
        prev = prev.next
    return dummy_head.next

def List2arr(head):
    # 复杂链表转二维数组
    arr_2 = []
    my_dict = {}
    curr = head
    i = 0
    while curr:
        tmp = [curr.val]
        arr_2.append(tmp)
        my_dict[curr] = i
        i += 1
        curr = curr.next
    curr = head
    while curr:
        # 添加random
        arr_2[my_dict[curr]].append(my_dict[curr.random] if curr.random else None)
        curr = curr.next
    return arr_2

class Solution:
    def copyRandomList(self, head) :
        # 前一种方法时复为O(n^2),主要是在寻找random指针时用时太久
        # 用空间换时间,在一次构造遍历时就存储好每个节点random指针所对应的复制节点
        # 建立哈希映射
        if not head:
            return None
        my_dict = {}
        curr = head
        while curr:
            my_dict[curr] = ListNode(curr.val)
            curr = curr.next
        # 对复制的链表进行next和random指针赋值
        curr = head
        while curr:
            # 这里不用my_dict[curr].next = my_dict[curr.next]
            # my_dict[curr].random = my_dict[curr.random]
            # 的原因是当curr.random为空时 是没有被存储下来 因此用get 若取不到则为None
            my_dict[curr].next = my_dict.get(curr.next)
            my_dict[curr].random = my_dict.get(curr.random)
            curr = curr.next
        return my_dict[head]

if __name__ == '__main__':
    # 复杂链表的构建,二维数组构建复杂链表,(用单链表用一维数组、双链表用二维数组构建的思想)
    arr_2 = [[7, None],[13, 0],[11, 4],[10, 2],[1, 0]]
    a = Solution()
    # 先将二维数组转为链表
    head = arr2List(arr_2)
    new_head = a.copyRandomList(head)
    # 再将链表转为二维数组
    new_arr2 = List2arr(new_head)
    print(new_arr2)

3. 思路三实现

class ListNode:
    def __init__(self, val , next = None, random = None):
        self.val = val
        self.next = next
        self.random = random

def arr2List(arr_2):
    # 二维数组构建复杂链表函数
    # 构建时将其存入哈希表
    my_dict = {}
    dummy_head = prev = ListNode(0)
    for i in range(len(arr_2)):
        my_dict[i] = prev.next = ListNode(arr_2[i][0])
        prev = prev.next
    # 至此dummy_head即为构建好的带有next的链表,但是没有random指针
    # 二维数组的第二个数字表示该节点random指针所指向的第几个节点
    prev = dummy_head.next
    for i in range(len(arr_2)):
        if arr_2[i][1] != None:
            prev.random = my_dict[arr_2[i][1]]
        else:
            prev.random = None
        prev = prev.next
    return dummy_head.next

def List2arr(head):
    # 复杂链表转二维数组
    arr_2 = []
    my_dict = {}
    curr = head
    i = 0
    while curr:
        tmp = [curr.val]
        arr_2.append(tmp)
        my_dict[curr] = i
        i += 1
        curr = curr.next
    curr = head
    while curr:
        # 添加random
        arr_2[my_dict[curr]].append(my_dict[curr.random] if curr.random else None)
        curr = curr.next
    return arr_2

class Solution:
    def copyRandomList(self, head):
        # 在不使用额外空间的情况下 时复为O(n).
        if not head:
            # 这里提前判断是为第三步时要赋予两个头节点
            return None
        # 1先将复制链表与原链表串联
        curr = head
        while curr:
            new_curr = ListNode(curr.val)
            new_curr.next = curr.next
            curr.next = new_curr
            curr = new_curr.next
        # 这样就将复制链表与原链表形成一个单链表,奇数节点为单链表(有random),偶数节点为复制链表(random未赋值)
        # 2为复制链表添加random(复制链表的节点特征为random为空)从不为空的random节点提取出random的next给next
        curr = head
        while curr:
            if curr.random:
                # 只要curr.random不为空,则其next的random与其random复制的节点同,即random.next
                curr.next.random = curr.random.next
            curr = curr.next.next   # 这里不curr=curr.next的原因是在执行时curr.next可能会空
        # 至此,就已连接好random,下面进行拆分并链接next
        # 3.现进行拆分并连接,构成next
        curr = head
        new_prev = new_curr = head.next
        while new_curr.next:
            curr.next = new_curr.next
            new_curr.next = new_curr.next.next
            curr, new_curr = curr.next, new_curr.next
        # 还原原链表结构(单独处理其尾节点)
        curr.next = None
        return new_prev

if __name__ == '__main__':
    # 复杂链表的构建,二维数组构建复杂链表,(用单链表用一维数组、双链表用二维数组构建的思想)
    arr_2 = [[7, None],[13, 0],[11, 4],[10, 2],[1, 0]]
    a = Solution()
    # 先将二维数组转为链表
    head = arr2List(arr_2)
    new_head = a.copyRandomList(head)
    # 再将链表转为二维数组
    new_arr2 = List2arr(new_head)
    print(new_arr2)

参考资料:

1. 《剑指offer》

2. 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

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

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

相关文章

幻兽帕鲁服务器多少钱?4核16G支持32人在线吗?

4核16G服务器是幻兽帕鲁Palworld推荐的配置,阿里云和腾讯云均推出针对幻兽帕鲁的4核16G服务器,阿里云4核16G幻兽帕鲁专属服务器32元1个月、66元3个月,腾讯云4核16G14M服务器66元1个月、277元3个月、1584元一年。云服务器吧yunfuwuqiba.com分享…

一、MongoDB、express的安装和基本使用

数据库【Sqlite3、MongoDB、Mysql】简介&小记 Sqlite3: SQLite3是一个轻量级的数据库系统,它被设计成嵌入式数据库。这意味着它是一个包含在应用程序中的数据库,而不是独立运行的系统服务。适用场景:如小型工具、游戏、本地…

node.js Redis SETNX命令实现分布式锁解决超卖/定时任务重复执行问题

Redis SETNX 命令背后的原理探究 当然,让我们通过一个简单的例子,使用 Redis CLI(命令行界面)来模拟获取锁和释放锁的过程。 在此示例中 获取锁: # 首先,设置锁密钥的唯一值和过期时间(秒) 127.0.0.1:6379> SET …

ChatGPT 官方中文页面上线

根据页面显示,OpenAI 现已推出 ChatGPT 的多语言功能 Alpha 版测试,允许用户选择不同语言的界面进行交互。 如下图所示,ChatGPT 会检测系统当前所使用的语言,并提示用户进行语言切换。 用户也可通过设置页面选择其他语言。目前&a…

企业转型:虚拟化对云计算的影响

虚拟化被认为是IT行业最优秀的技术之一。虚拟化提供的灵活性和效率,有助于企业根据不断变化的需求扩展其IT基础设施。虚拟化是云基础设施的基础,允许按需动态分配和管理计算资源。这种适应性对于满足现代企业的多样化需求至关重要,因为现代企…

深度学习之处理多维特征的输入

我们首先来看一个糖尿病的数据集: 在数据集中,我们称每一行叫做sample,表示一个样本,称每一列是feature,也就是特征在数据库里面这就是一个关系表,每一行叫做记录,每一列叫做字段。 每一个样本都…

山西电力市场日前价格预测【2024-01-29】

日前价格预测 预测说明: 如上图所示,预测明日(2024-01-29)山西电力市场全天平均日前电价为279.99元/MWh。其中,最高日前电价为397.38元/MWh,预计出现在07:45。最低日前电价为0.00元/MWh,预计出…

【计算机专业学习委员必备自动化催作业通知】

文章目录 前言一、前期准备zfile部署mysql服务搭建 二、编写python脚本python代码 三、总结 前言 大家好!我是一名计算机专业的菜鸟,作为这个专业的学习委员,我觉得收电子版作业是一件非常麻烦的事情,作业实验科目也比较多&#…

RLHF学习

整体流程 三个步骤分解: 预训练一个语言模型 (LM) ;聚合问答数据并训练一个奖励模型 (Reward Model,RM) ;用强化学习 (RL) 方式微调 LM。 RW RM 的训练是 RLHF 区别于旧范式的开端。这一模型接收一系列文本并返回一个标量奖励&…

探索Pyecharts关系图绘制技巧:炫酷效果与创意呈现【第42篇—python:Pyecharts水球图】

文章目录 Pyecharts绘制多种炫酷关系网图引言准备工作代码实战1. 基本关系网图2. 自定义节点样式和边样式3. 关系网图的层级结构4. 添加标签和工具提示5. 动态关系网图6. 高级关系网图 - Les Miserables 示例7. 自定义关系网图布局8. 添加背景图9. 3D 关系网图10. 热力关系网图…

使用PCL进行法向量可视化

使用PCL进行法向量可视化 文章目录 1、使用PCL进行法向量可视化2、计算所有点的法线并显示3、计算一个子集的法线 1、使用PCL进行法向量可视化 #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/visualization/pcl_visualizer.h> #include &l…

Qt使用中文字符串乱码的问题

文章目录 vs编译器下第一种解决方式第二种解决方式 Qt编译器下 我们在使用qt的时候有时候会遇到打印中文字符串的时候出现中文乱码的问题&#xff0c;主要是由于Qt的QString字符串存储的方式是使用utf-8的编码方式&#xff0c;如果我们本地的文件是使用GBK方式的编码再使用中文…

DAY09_SpringBoot—整合SpringMVCSpringMVC参数取值用法

目录 1 SpringMVC1.1 SpringMVC框架介绍1.2 SpringMVC入门案例1.2.1 创建项目1.2.2 添加依赖项1.2.3 检查pom.xml文件1.2.4 编辑YML配置文件1.2.5 在templates中添加index.html文件1.2.6 默认页面跳转机制 1.3 RequestMapping注解测试1.3.1 编辑HelloController1.3.2 页面请求效…

【计算机网络】深入掌握计算机网络的核心要点(面试专用)

写在前面 前言四层模型网络地址管理Linux下设置ipARP请求包总结 前言 计算机网络是指将分散的计算机设备通过通信线路连接起来&#xff0c;形成一个统一的网络。为了使得各个计算机之间能够相互通信&#xff0c;需要遵循一定的协议和规范。OSI参考模型和TCP/IP参考模型是计算机…

32GPIO输入&按键控制LED&光敏控制蜂鸣器

一.硬件 光线越强&#xff0c;光敏电阻的阻值越小 温度越高&#xff0c;热敏电阻的阻值就越小 红外光线越强&#xff0c;红外接收管的阻值就越小 类比&#xff1a;电阻阻值越小&#xff0c;上拉或下拉就越强 &#xff08;弹簧的拉力就越强&#xff09; 在上下的电阻分压下&a…

FPGA HDMI IP之DDC(本质I2C协议)通道学习

目的&#xff1a; 使用KingstVIS逻辑分析仪软件分析HDMI的DDC通道传输的SCDC数据&#xff08;遵循I2C协议&#xff09;&#xff0c;同时学习了解SCDC的寄存器与I2C通信协议。 部分英文缩写&#xff1a; HDMIHigh Definition Multi-media Interface高清多媒体接口DDCDisplay Dat…

CSS基础细节学习

目录 一.CSS--网页的美容师 二.语法规范及选择器的介绍 一.CSS--网页的美容师 CSS是层叠样式表( Cascading Style Sheets )的简称&#xff0c;有时我们也会称之为CSS样式表或级联样式表。 CSS是也是一种标记语言&#xff0c;CSS主要用于设置HTML页面中的文本内容(字体、大小…

自定义实现 View.DragShadowBuilder 设置拖拽视图的大小

直接上刺刀 /*** Desc : 自定义拖拽视图的大小*/ public class CustomDragShadowBuilder extends View.DragShadowBuilder {private double mShadowSize;private Point mScaleFactor;/*** param view 需要拖拽的view* param shadowSize 拖拽视图的放大倍数*/public Cus…

CUDA下载安装教程,新手详细

目录 一、下载二、安装三、 设置环境变量四、补丁安装 由于项目需要安装特定版本的CUDA&#xff0c;现记录安装过程。 一、下载 进入官方下载地址&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive 选择自己需要的版本。如果没有明确要求版本号&#xff0c;那么…

智能AI系统开发,专业软件硬件物联网开发公司,探索未来科技新纪元

在信息时代&#xff0c;人工智能&#xff08;AI&#xff09;、物联网等前沿技术日益受到人们的关注。智能AI系统、专业软件硬件物联网开发公司应运而生。今天&#xff0c;我们将向大家介绍一家位于XX城的专业公司&#xff0c;致力于智能AI系统开发和软件硬件物联网领域的创新研…