题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。
复杂链表结点定义如下:
class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
题解:
解法一:
分成两步:第一步复制原链表上的每一个结点,并用 next 链接起来;第二步是设置每个结点的 random 指针。
设原始链表结点 N 的random 指针指向的结点为 S,S可能位于N的前面,也可能位于N的后面。所以需要从链表头开始查找,那么在复制链表相应的 N’ 结点的random 指针指向的 S’ 结点时,从头结点走相同的距离即可。
对于每个含有 n 个结点的链表,由于定位每个结点的 random 都需要从链表头结点开始经过 O(n)步才能找到,因此这种方法时间复杂度为 O(n^2)
解法二:
以空间换时间
在第一步时,将每个结点与其复制结点的匹配信息存到一个哈希表中,< N,N’ >; 在第二步中,我们根据原链表 N 的 random 指向的结点 S,不用遍历链表查找S在链表中的位置来确定 S‘,只需从 hash 表中 直接获取 S‘ ,即可将 N’ 的random指向 S‘ 。总的时间复杂度为 O(n)。
class Solution {
public RandomListNode Clone(RandomListNode pHead) {
if (pHead == null) {
return pHead;
}
HashMap<RandomListNode,RandomListNode> cache=new HashMap();
RandomListNode pHeadCopy=new RandomListNode(pHead.label);//保留复制结点头
RandomListNode pNode = pHead;//指向原结点
RandomListNode pNodeCopy=pHeadCopy;//指向复制结点
cache.put(pNode, pNodeCopy);//保存原结点和它复制的映射
while(pNode.next!=null){
pNode=pNode.next;
RandomListNode node=new RandomListNode(pNode.label);
pNodeCopy.next=node;
pNodeCopy=pNodeCopy.next;
cache.put(pNode, pNodeCopy);//保存原结点和它复制的映射
}
//关联random指针
pNode = pHead;//重新指向原链表头
pNodeCopy=pHeadCopy;//重新指向复制链表头
while(pNode!=null){
pNodeCopy.random=cache.get(pNode.random);
pNode=pNode.next;
pNodeCopy=pNodeCopy.next;
}
return pHeadCopy;
}
}
解法三:
不用辅助空间,实现 O(n) 的时间效率。
第一步仍然是根据原始链表的每个结点N 创建对应的 N‘ 。这一次把 N‘ 链接在N的后面。
第二步设置复制出来的结点的 random。
第三步把长链表拆分成两个链表:把奇数位置的结点用 next 链接起来就是原始链表,把偶数位置的结点用 next 链接起来就是复制出来的链表。
public class Solution {
public RandomListNode Clone(RandomListNode pHead) {
if (pHead == null) {
return pHead;
}
// 1.将复制结点连到相应结点的后面
RandomListNode pNode = pHead;// 遍历用的指针
while (pNode != null) {
RandomListNode node = new RandomListNode(pNode.label);
node.next = pNode.next;
pNode.next = node;
pNode = node.next;
}
// 2.设置random指针
pNode = pHead;// 重新指向链表头
while (pNode != null) {
if(pNode.random!=null){//如果有random
pNode.next.random = pNode.random.next;// 复制链表的random指针指向,原链表的random指向的结点的下一个结点
}
pNode = pNode.next.next;
}
// 3.拆分两个链表,奇数位置为原链表,偶数位置为新链表
pNode = pHead;// 重新指向链表头
RandomListNode pHeadCopy = pNode.next;//保留复制链表头
RandomListNode pNodeCopy=pHeadCopy;//指向复制结点
pNode.next=pNodeCopy.next;
pNode=pNode.next;
while(pNode!=null){
pNodeCopy.next=pNode.next;//pNodeCopy连接下一个偶数结点,并前移
pNodeCopy=pNodeCopy.next;
pNode.next=pNodeCopy.next;//pNode 连接下一个奇数结点,并前移
pNode=pNode.next;
}
return pHeadCopy;
}
}