三次优化剑指 Offer 35. 复杂链表的复制
Posted 来老铁干了这碗代码
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了三次优化剑指 Offer 35. 复杂链表的复制相关的知识,希望对你有一定的参考价值。
立志用最少代码做最高效的表达
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。
示例 1:
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:
输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:
输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:
输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。
提示:
-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。
思路一:HashMap
思路:以map将新节点和旧节点映射起来,进而建立链表。
步骤:
1、完成以next指针为串联的链表的建立,并建立HashMap映射
2、完成random的链表建设
缺点:冗余
class Solution {
public Node copyRandomList(Node head) {
/**
1、根据next完成链表的复制,同时建立hash表映射
2、根据hash表的映射进行random的指向
*/
if(head == null) return null; //头结点为空的情况
Map<Node, Node> map = new HashMap<>(); // 建立Map映射,映射原节点和被复制的节点
Node copyListHead = new Node(head.val); // 复制链表的头结点
map.put(head, copyListHead); // 建立头结点间的映射
Node oldTmpList1 = head.next; // 原链表的临时遍历节点
Node copyTmpList1 = copyListHead; // 复制链表的临时遍历节点1
// 1、完成next的复制
while(oldTmpList1 != null) {
Node newTmpList1 = new Node(oldTmpList1.val); // 赋值链表的临时遍历节点2(因为建表需要两个节点)
map.put(oldTmpList1, newTmpList1); // 建立映射
copyTmpList1.next = newTmpList1; // 建表过程
copyTmpList1 = newTmpList1;
oldTmpList1 = oldTmpList1.next;
}
// 2、完成random的复制
Node oldTmpList2 = head;
Node newTmpList2 = copyListHead; // 复制链表的临时遍历节点1
while(oldTmpList2 != null) {
if(oldTmpList2.random != null) {
newTmpList2.random = map.get(oldTmpList2.random);
}
oldTmpList2 = oldTmpList2.next;
newTmpList2 = newTmpList2.next;
}
return copyListHead;
}
}
思路2:对思路1的优化
拆分成两步:
1、将新旧节点映射起来
2、进行next和random的链表建立
时间复杂度:O(n) 、 空间复杂度:O(n)
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null; //头结点为空的情况
Map<Node, Node>map = new HashMap<>(); // 建立Map映射,映射原节点和被复制的节点
Node copyListHead = new Node(head.val); // 复制链表的头结点
// 1、进行Map映射
for(Node tmp = head; tmp != null; tmp = tmp.next) {
if(tmp == head) map.put(head, copyListHead); // 头结点单独判断
else map.put(tmp, new Node(tmp.val)); // 建立映射
}
// 2、完成next的复制和random的复制
for(Node tmp = head; tmp != null; tmp = tmp.next) {
map.get(tmp).next = map.get(tmp.next);
map.get(tmp).random = map.get(tmp.random);
}
return copyListHead;
}
}
代码3:原地复制
思路:在原链表的每个节点后都构建一个新节点,将原来形如A->B->C
的链表改为A->A'->B->B'->C->C'
,然后拆分两链表即可。
步骤:
1、复制各个节点,构建拼接链表
2、构建各个新节点的random指针
3、拆分成两个链表
注意:本题的题意是深拷贝,即不能更改原链表。
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
Node cur = head;
// 1. 复制各节点,并构建拼接链表
while(cur != null) {
Node tmp = new Node(cur.val);
tmp.next = cur.next;
cur.next = tmp;
cur = tmp.next;
}
// 2. 构建各新节点的 random 指向
cur = head;
while(cur != null) {
if(cur.random != null)
cur.next.random = cur.random.next;
cur = cur.next.next;
}
// 3. 拆分两链表
cur = head.next;
Node pre = head, res = head.next;
while(cur.next != null) {
pre.next = pre.next.next;
cur.next = cur.next.next;
pre = pre.next;
cur = cur.next;
}
pre.next = null; // 单独处理原链表尾节点
return res; // 返回新链表头节点
}
}
以上是关于三次优化剑指 Offer 35. 复杂链表的复制的主要内容,如果未能解决你的问题,请参考以下文章