题目描述[微软苏州]
一条带随机指针的链表,对于每个节点,除了next
指针指向下一个节点以外,还带一个randNext
指针指向链表中任何一个节点或空。求对这个链表进行深复制,即复制出的链表和原来链表具有完全相同的结构,但是与原链表完全无关。
分析1
此题难点在于新建链表节点的randNext
不能立即在新链表中找到指向,如果要记录randNext
指向节点相对于自身的位置,那么每个节点都需要有一个遍历计数过程,时间复杂度为
randNext
指向的节点就成为了关键步骤。
使用链表的问题在于查找过程需要遍历,从而导致了效率低下,所以可以想到使用哈希来快速查找所需要的节点。这里HashMap
记录旧节点和新节点之间的映射关系,一旦得到了一个旧节点,就可以立刻找到新节点,查找的复杂度为
HashMap
,一次复制链接。
代码1
static Node copyWithHash(Node headNode) {
Map<Node, Node> map = new HashMap<>();
Node oldNode = headNode;
// 建立新节点与原节点的映射
while (oldNode != null) {
map.put(oldNode, new Node(oldNode.data + 100));// 测试
oldNode = oldNode.next;
}
oldNode = headNode;
Node newHeadNode = null;
Node newNode = null;
// 利用映射将旧链接关系复制到新链接关系
while (oldNode != null) {
newNode = map.get(oldNode);
newNode.next = map.get(oldNode.next);
newNode.randNext = map.get(oldNode.randNext);// get(null)仍返回null
if (newHeadNode == null) newHeadNode = newNode;
oldNode = oldNode.next;
}
return newHeadNode;
}
分析2
使用HashMap
的空间复杂度为
next
就可以找到新节点了。只是复制过程复杂一些,需要三次遍历才可以:第一次插入新节点到原链表中,即newNode.next = oldNode.next; oldNode.next = newNode
,第二次复制链接关系,即newNode.randNext = oldNode.randNext.next
,最后一次从原链表分离出新链表,即oldNode.next = newNode.next; newNode.next = newNode.next.next
。
代码2
static Node copyInsertion(Node headNode) {
// 第一遍将新节点链接到旧节点next后,并链接到下一个旧节点
Node oldNode = headNode;
while (oldNode != null) {
Node newNode = new Node(oldNode.data + 100);// 测试
newNode.next = oldNode.next;
oldNode.next = newNode;
oldNode = newNode.next;
}
// 第二遍将旧节点randNext链接关系复制到新节点上
oldNode = headNode;
while (oldNode != null) {
Node newNode = oldNode.next;
if (oldNode.randNext != null){
newNode.randNext = oldNode.randNext.next;
}
oldNode = newNode.next;
}
// 第三遍将新节点与旧节点分离开
Node newHeadNode = null;
oldNode = headNode;
while (oldNode != null) {
Node newNode = oldNode.next;
oldNode.next = newNode.next;
oldNode = oldNode.next;
if (oldNode != null) newNode.next = oldNode.next;
if (newHeadNode == null) newHeadNode = newNode;
}
return newHeadNode;
}
测试
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class RandLinked {
static class Node {
int data;
Node next = null, randNext = null;
Node(int data) {
this.data = data;
}
}
static Node buildRandLinked(int[] data) {
int length = data.length;
Map<Integer, Node> map = new HashMap<>(length);
for (int i : data) {
map.put(i, new Node(i));
}
int[] randIndex = new int[length];
Random random = new Random();
for (int i = 0; i < length; i++) {
randIndex[i] = random.nextInt(length);
}
Node headNode = map.get(data[0]);
Node tmpNode = null;
for (int i = 0; i < length - 1; i++) {
tmpNode = map.get(data[i]);
tmpNode.next = map.get(data[i + 1]);
if (randIndex[i] % 4 == 0)
tmpNode.randNext = null;
else
tmpNode.randNext = map.get(data[randIndex[i]]);
}
return headNode;
}
static void printRandLinked(Node headNode) {
Node tmpNode = headNode;
while (tmpNode != null) {
System.out.print(tmpNode.data + " -> ");
System.out.println(tmpNode.randNext == null ? null : tmpNode.randNext.data);
tmpNode = tmpNode.next;
}
}
public static void main(String[] args) {
Node headNode = buildRandLinked(new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
printRandLinked(headNode);
System.out.println("-----------");
printRandLinked(copyInsertion(headNode));
}
} /* result
0 -> null
1 -> 2
2 -> 3
3 -> 9
4 -> 6
5 -> null
6 -> 7
7 -> 9
8 -> 6
9 -> null
-----------
100 -> null
101 -> 102
102 -> 103
103 -> 109
104 -> 106
105 -> null
106 -> 107
107 -> 109
108 -> 106
109 -> null
*/