随笔 - 54  文章 - 0  评论 - 184  阅读 - 58万

链表的复制

问题描述

这是从微博上看到的一个面试问题,描述如下:

给一个链表,如下定义:

1 struct Node
2 {
3    struct Node* next;
4    struct Node* random;
5    void*data;
6 };

 

 其中random 指向链表中的任意一个节点或为空。

 

 现在要求对一个这种链表进行深度复制(即复制得到的链表中节点的next, random指向新链表中的相对应位置)。

 如下图, 第一个是原链表,第二个是复制后的链表,现在要求尽可能快、省地完成这个复制过程。

简单分析

这个问题的难点显然就在于怎么设置新链表节点的random指针。

一种比较直观的解法是类似于深度优先进行复制:

1) 复制节点A1,得到B1

2) 复制节点A1的random指向的节点NA1.

3) A1 = A1->next。

4) 如果A1不为空,重复以上动作。

但是上面的做法有问题,因为random是随意指向链表中的节点,复制random与复制next的过程有重复,而上面的做法却没有检查哪些节点是已经复制过的了。

为解决这个问题,我们可以在复制的过程中引入一个hash,用来保存哪些节点已经被复制过了。

1) 检查hash,如果A1不在表中,对A1进行复制,得到B1, 并把A1放进hash表。

2) 检查A1的random指向的节点NA1是否在hash中,如果NA1不在表中,对NA1进行复制,到NB1, 并把NA1放入hash表中。

3) A1 = A1->next;

4) 如果A1不为空,重复以上动作。

以上做法如果忽视检查hash的时间消耗的话,时间复杂度为O(n), 但是空间复杂度也是O(n).

这个解决也可以接受了,也是比较容易想到的。

巧解

但事实还有一个比较巧的做法,细想一下,设置random的关键是新链表节点与旧链表节点之间要能建立起对应的关系。

这个关系应该怎样建呢? 请看下图

如图,蓝色节点是旧链表中的节点,紫色节点是新复制的节点,红色线条是旧节点中的next,绿色线条是新节点中的next,蓝色线条是新节点的random,黑色线条是旧节点的random。

算法如下 :

1) 扫描旧链表,逐个节点复制,得到上图中的长链表, 时间空间复杂度均为O(n)

2) 注意到上一步建立的新链表中,每个旧节点都指向了相对应的新节点。

     剩下的问题就是怎么设置新节点的rando,现在开始设置新节点的random,及设置好新旧节点的next.

     令A = A1;

     a) 找到旧节点A的random指向的NA

     b) B=A->next, B1是A1对应的新节点

     c) NB = NA->next.

     d) A->next = B->next;  B->random = NB; B->next = A->next->next;

     e) A = A->next

     f) 如果A不为空,重复以上动作。

 

这个解法比较巧妙,它的优点是时间复杂度为O(n), 且空间复杂度为O(1).

不好的地方嘛--这个解法需要修改原来的链表。

 

posted on   twoon  阅读(9840)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示