Python算法题集_随机链表的复制

 Python算法题集_随机链表的复制

  • 题138:随机链表的复制
  • 1. 示例说明
  • 2. 题目解析
    • - 题意分解
    • - 优化思路
    • - 测量工具
  • 3. 代码展开
    • 1) 标准求解【双层循环】
    • 2) 改进版一【字典哈希】
    • 3) 改进版二【单层哈希】
    • 4) 改进版三【递归大法】
  • 4. 最优算法

本文为Python算法题集之一的代码示例

题138:随机链表的复制

1. 示例说明

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

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

    例如,如果原链表中有 XY 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 xy ,同样有 x.random --> y

    返回复制链表的头节点。

    用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

    • val:一个表示 Node.val 的整数。
    • random_index:随机指针指向的节点索引(范围从 0n-1);如果不指向任何节点,则为 null

    你的代码 接受原链表的头节点 head 作为传入参数。

    示例 1:

    img

    输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
    输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
    

    示例 2:

    img

    输入:head = [[1,1],[2,1]]
    输出:[[1,1],[2,1]]
    

    示例 3:

    img

    输入:head = [[3,null],[3,0],[3,null]]
    输出:[[3,null],[3,0],[3,null]]
    

    提示:

    • 0 <= n <= 1000
    • -104 <= Node.val <= 104
    • Node.randomnull 或指向链表中的节点。

2. 题目解析

- 题意分解

  1. 本题为对链表中的节点进行复制,节点包括下一节点链接和随机节点链接
  2. 本题的主要计算是2块,1是链表遍历,2是随机节点链接检索
  3. 基本的解法是双层循环,复制链表1层循环,随机节点检索1层循环,所以基本的时间算法复杂度为O(n^2)

- 优化思路

  1. 通常优化:减少循环层次

  2. 通常优化:增加分支,减少计算集

  3. 通常优化:采用内置算法来提升计算速度

  4. 分析题目特点,分析最优解

    1. 标准方法是双层循环,先复制,再检索

    2. 可以用哈希法【字典】优化查询

    3. 可以用递归法进行节点复制的解析,但是递归法有最大层次,超过就会报错


- 测量工具

  • 本地化测试说明:LeetCode网站测试运行时数据波动很大,因此需要本地化测试解决这个问题
  • CheckFuncPerf(本地化函数用时和内存占用测试模块)已上传到CSDN,地址:Python算法题集_检测函数用时和内存占用的模块
  • 本题很难超时,本地化超时测试用例自己生成,详见【最优算法章节】

3. 代码展开

1) 标准求解【双层循环】

外层遍历,内层检索,网站性能良好,本地性能不堪

性能良好,超越83%在这里插入图片描述

import CheckFuncPerf as cfp

class Solution:
 @staticmethod
 def copyRandomList_base(head):
     if not head: return None
     list_origin, idx_origin = [], []
     while head:
         list_origin.append(head)
         idx_origin.append(-1)
         head = head.next
     ilen = len(list_origin)
     for iIdx in range(ilen):
         if list_origin[iIdx].random:
             idx_origin[iIdx] = list_origin.index(list_origin[iIdx].random)
     list_dist = []
     for iIdx in range(ilen):
         tmpNode = Node(list_origin[iIdx].val)
         list_dist.append(tmpNode)
     for iIdx in range(ilen-1):
         list_dist[iIdx].next = list_dist[iIdx+1]
     for iIdx in range(ilen):
         if idx_origin[iIdx] > -1:
             list_dist[iIdx].random = list_dist[idx_origin[iIdx]]
     return list_dist[0]

result = cfp.getTimeMemoryStr(Solution.copyRandomList_base, ahead)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))

# 运行结果
函数 copyRandomList_base 的运行时间为 115717.23 ms;内存使用量为 17536.00 KB 执行结果 = 0

2) 改进版一【字典哈希】

双字典,将原链表、复制链表均存入字典,通过哈希优化检索

性能良好,超过81%在这里插入图片描述

import CheckFuncPerf as cfp

class Solution:
 @staticmethod
 def copyRandomList_ext1(head):
     if not head: return None
     dict_origin = {} 
     dict_new = {}  
     nodepre = Node(0) 
     newnode = nodepre
     originhead = head 
     idx = 0 
     while originhead:
         dict_origin[originhead] = idx 
         newnode.next = Node(originhead.val)
         newnode = newnode.next
         dict_new[idx] = newnode 
         idx += 1 
         originhead = originhead.next
     dict_new[idx] = None  
     newnode = nodepre.next
     originhead = head
     while originhead:
         random_node = originhead.random
         nodepos = dict_origin[random_node] if random_node else idx
         node = dict_new[nodepos] 
         newnode.random = node
         newnode = newnode.next
         originhead = originhead.next
     return nodepre.next  

result = cfp.getTimeMemoryStr(Solution.copyRandomList_ext1, ahead)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))

# 运行结果
函数 copyRandomList_ext1 的运行时间为 390.09 ms;内存使用量为 19492.00 KB 执行结果 = 0

3) 改进版二【单层哈希】

将原链表、复制链表存入一个字典,通过哈希优化定位

马马虎虎,超过69%在这里插入图片描述

import CheckFuncPerf as cfp

class Solution:
 @staticmethod
 def copyRandomList_ext2(head):
     if not head: return None
     curnode = head
     dict_nodes = {}  
     while curnode:
         dict_nodes[curnode] = Node(curnode.val)
         curnode = curnode.next  
     curnode = head
     while curnode:
         dict_nodes[curnode].next = dict_nodes[curnode.next] if curnode.next else None
         dict_nodes[curnode].random = dict_nodes[curnode.random] if curnode.random else None
         curnode = curnode.next
     return dict_nodes[head]

result = cfp.getTimeMemoryStr(Solution.copyRandomList_ext2, ahead)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))

# 运行结果
函数 copyRandomList_ext2 的运行时间为 170.04 ms;内存使用量为 12820.00 KB 执行结果 = 0

4) 改进版三【递归大法】

采用递归方式进行节点复制,本地性能都不好,不管是否报错

性能优良,超越85%在这里插入图片描述

import CheckFuncPerf as cfp

class Solution:
 @staticmethod
 def copyRandomList_ext3(head):
     def copyNode(head, dict_nodes):
         if not head: return None
         while head not in dict_nodes.keys():
             headNew = Node(head.val)  # 拷贝新节点
             dict_nodes[head] = headNew  # 记录到哈希表中
             headNew.next = copyNode(head.next, dict_nodes)
             headNew.random = copyNode(head.random, dict_nodes)
         return dict_nodes[head]
     dict_nodes = {}
     return copyNode(head, dict_nodes)

result = cfp.getTimeMemoryStr(Solution.copyRandomList_ext3, ahead)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))

# 运行结果
Traceback (most recent call last):
 ......
[Previous line repeated 991 more times]
RecursionError: maximum recursion depth exceeded

4. 最优算法

根据本地日志分析,最优算法为第3种copyRandomList_ext2

ilen = 100000
list_node =[]
for iIdx in range(ilen):
    tmpListnode = Node(iIdx)
    list_node.append(tmpListnode)
for iIdx in range(ilen-1):
    list_node[iIdx].next = list_node[iIdx+1]
for iIdx in range(ilen):
    list_node[iIdx].random = list_node[random.randint(1, ilen)-1]
ahead = list_node[0]
result = cfp.getTimeMemoryStr(Solution.copyRandomList_base, ahead)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))

# 算法本地速度实测比较
# 链表长度900
函数 copyRandomList_base 的运行时间为 10.00 ms;内存使用量为 296.00 KB 执行结果 = 0
函数 copyRandomList_ext1 的运行时间为 1.00 ms;内存使用量为 196.00 KB 执行结果 = 0
函数 copyRandomList_ext2 的运行时间为 1.00 ms;内存使用量为 128.00 KB 执行结果 = 0
函数 copyRandomList_ext3 的运行时间为 2.00 ms;内存使用量为 1104.00 KB 执行结果 = 0
# 链表长度10W
函数 copyRandomList_base 的运行时间为 115717.23 ms;内存使用量为 17536.00 KB 执行结果 = 0
函数 copyRandomList_ext1 的运行时间为 390.09 ms;内存使用量为 19492.00 KB 执行结果 = 0
函数 copyRandomList_ext2 的运行时间为 170.04 ms;内存使用量为 12820.00 KB 执行结果 = 0
Traceback (most recent call last):    # 递归法 copyRandomList_ext3 超时
    ......
  [Previous line repeated 991 more times]
RecursionError: maximum recursion depth exceeded

一日练,一日功,一日不练十日空

may the odds be ever in your favor ~

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

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

相关文章

Python 错误及其解决方法

Python 是一种易于学习的编程语言&#xff0c;但初学者在学习和使用 Python 的过程中难免会遇到一些错误。以下是一些常见的 Python 错误及其解决方法&#xff1a; 1. 语法错误&#xff08;SyntaxError&#xff09;&#xff1a; python # 错误示例 print("Hello, World!…

elasticsearch下载及可视化工具下载使用

elasticsearch下载及配置、启动 一、下载 Download Elasticsearch | Elastic 二、启动 双击bat即可。 出现如下说明启动成功&#xff1a; 访问测试&#xff1a; 三、注意 &#xff08;1&#xff09;因为es启动默认端口是&#xff1a;9200,所以需要检查此端口是否被占用。…

js中bind、call、apply 区别(如何实现)

文章目录 一、作用二、区别applycallbind小结 三、实现 一、作用 call、apply、bind作用是改变函数执行时的上下文&#xff0c;简而言之就是改变函数运行时的this指向 那么什么情况下需要改变this的指向呢&#xff1f;下面举个例子 var name "lucy"; var obj {n…

python统计分析——两样本t检验

参考资料&#xff1a;用python动手学统计学 1、导入库 # 导入库 # 用于数值计算的库 import numpy as np import pandas as pd import scipy as sp from scipy import stats # 用于绘图的库 from matplotlib import pyplot as plt import seaborn as sns sns.set() 2、准备数…

【HTTP】localhost和127.0.0.1的区别是什么?

目录 localhost是什么呢&#xff1f; 从域名到程序 localhost和127.0.0.1的区别是什么&#xff1f; 域名的等级划分 多网站共用一个IP和端口 私有IP地址 IPv6 今天在网上逛的时候看到一个问题&#xff0c;没想到大家讨论的很热烈&#xff0c;就是标题中这个&#xff1a; …

基于微信小程序的校园故障维修管理系统的研究与实现

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

闭区间上连续函数的性质【高数笔记】

1. 分几个性质 2. 每个性质的注意事项是什么 3. 每个性质适用什么类型的题型 4. 注意最值定理和正弦函数的不同 5. 做题步骤是什么

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

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

异地的文件如何共享?

在现代工作中&#xff0c;随着互联网的普及和信息化的进步&#xff0c;越来越多的人需要在异地进行文件共享和协同办公。无论是企业的异地分支机构&#xff0c;还是多个个人之间的跨地合作&#xff0c;通过异地的文件共享可以极大地提高工作效率和协作效果。本文将介绍一种名为…

C# CAD交互界面-自定义面板集-添加快捷命令(五)

运行环境 vs2022 c# cad2016 调试成功 一、引用 using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Runtime; using Autodesk.AutoCAD.Windows; using System; using System.Drawing; using System.Windows.Forms; 二、代码说明 [CommandMethod("Cre…

【Python 千题 —— 基础篇】查找年龄

Python 千题持续更新中 …… 脑图地址 👉:⭐https://twilight-fanyi.gitee.io/mind-map/Python千题.html⭐ 题目描述 题目描述 班级中有 Tom、Alan、Bob、Candy、Sandy 五个人,他们组成字典 {Tom: 23, Alan: 24, Bob: 21, Candy: 22, Sandy: 21},字典的键是姓名,字典的…

【FPGA开发】Modelsim和Vivado的使用

本篇文章包含的内容 一、FPGA工程文件结构二、Modelsim的使用三、Vivado的使用3.1 建立工程3.2 分析 RTL ANALYSIS3.2.1 .xdc约束&#xff08;Constraints&#xff09;文件的产生 3.3 综合 SYNTHESIS3.4 执行 IMPLEMENTATION3.5 烧录程序3.6 程序固化3.6.1 SPI约束3.6.2 .bin文…

postgresql 手动清理wal日志的101个坑

新年的第一天&#xff0c;总结下去年遇到的关于WAL日志清理的101个坑&#xff0c;以及如何相对安全地进行清理。前面是关于WAL日志堆积的原因分析&#xff0c;清理相关可以直接看第三部分。 首先说明&#xff0c;手动清理wal日志是一个高风险的操作&#xff0c;尤其对于带主从的…

《CSS 简易速速上手小册》第6章:高级 CSS 技巧(2024 最新版)

文章目录 6.1 使用 CSS 变量进行设计&#xff1a;魔法配方的调配6.1.1 基础知识6.1.2 重点案例&#xff1a;创建可定制的主题6.1.3 拓展案例 1&#xff1a;响应式字体大小6.1.4 拓展案例 2&#xff1a;使用 CSS 变量创建动态阴影效果 6.2 calc(), min(), max() 等函数的应用&am…

AES加密中的CBC和ECB

目录 1.说明 2.ECB模式&#xff08;base64&#xff09; 3.CBC模式 4.总结 1.说明 AES是常见的对称加密算法&#xff0c;加密和解密使用相同的密钥&#xff0c;流程如下&#xff1a; 主要概念如下&#xff1a; ①明文 ②密钥 用来加密明文的密码&#xff0c;在对称加密算…

CTF-PWN-沙箱逃脱-【侧信道爆破】(2021-蓝帽杯初赛-slient)

文章目录 侧信道攻击测信道爆破2021-蓝帽杯初赛-slient先自己准备个flag检查沙箱IDA源码mainsub_A60() 相关知识size_t getpagesize(void)void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);range(i,j)编写相关shellcode发现"的用法此时…

什么是宿主软件?宿主软件有哪些?

什么是宿主软件? 宿主软件就是专业的音乐制作软件&#xff0c;我们日常听到的大多数正规音乐作品都是用宿主软件制作出来的&#xff0c;这些软件一般需要安装各类插件&#xff0c;插件就像寄生虫需要在宿主软件里加载才可以工作。 插件主要分虚拟乐器插件和音频处理插件两类…

JZ36 二叉搜索树与双向链表

目录 题目描述 二叉搜索树与双向链表_牛客题霸_牛客网 题目解析 题目答案 最后 题目描述 二叉搜索树与双向链表_牛客题霸_牛客网 题目解析 这里采用的是采用前序遍历的思想&#xff0c;找到要转换的双向链表的头节点也就是这个二叉搜索树的最左节点&#xff0c;找到之后依…

MySQL篇----第十九篇

系列文章目录 文章目录 系列文章目录前言一、什么是存储过程?用什么来调用?二、如何通俗地理解三个范式?三、什么是基本表?什么是视图?四、试述视图的优点?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这…

力扣[面试题 01.02. 判定是否互为字符重排(哈希表,位图)

Problem: 面试题 01.02. 判定是否互为字符重排 文章目录 题目描述思路复杂度Code 题目描述 思路 思路1&#xff1a;哈希表 1.若两个字符串长度不相等&#xff0c;则一定不符合题意&#xff1b; 2.创建一个map集合&#xff0c;先将字符串s1中的每一个字符与其对应的数量存入集合…