题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
首先第一种想法是分两步进行,首先复制原始链表的每个节点,通过.next连接起来,然后设置每个节点的random,复杂的就是这一步,因为无法确定random的位置,所以需要从头节点开始找,对于原始链表的每个节点,找到它的random从头节点走了多少步,那新链表对应节点的random也需要从头节点走多少步。这样就找到了对应的random。这一步每个节点random都需要n步,共n个节点,所以时间复杂度是O(n^2);
由此分析可以看出,我们主要的时间花费在了找random上,所以我们想办法优化random的查找,我们在这里不采用暴力查找法,使用空间换时间的思路。试想一下,如果我们可以根据原始链表节点的random节点直接定位到新链表的random节点,不是就不需要从头查找random节点了么?
所以办法就来了,那就是对原始链表的每个节点,都和新链表的每个节点建立一一对应的关系即可,当然我们想到map最合适,这样就实现了O(1)的时间复杂度。
上代码:
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
HashMap<Integer, RandomListNode> map = new HashMap<>();
Stack<RandomListNode> stack = new Stack<>();
Stack<Integer> stack_random = new Stack<>();
RandomListNode nHead = new RandomListNode(-1);
RandomListNode tail = nHead;
RandomListNode next = pHead;
while(next != null){
tail.next = new RandomListNode(next.label);
tail = tail.next;
map.put(tail.label, tail);
if(next.random != null){
RandomListNode random = map.get(next.random.label); //看是否已经创建了
if(random != null){
tail.random = random;
}
else{
stack.push(tail);
stack_random.push(next.random.label);
}
}
next = next.next;
}
while(!stack.isEmpty()){
stack.pop().random = map.get(stack_random.pop());
}
return nHead.next;
}
}
上面的代码虽然把时间复杂度降下来了,但是空间复杂度上去了,我们接下来再想想能不能在优化一下空间复杂度呢。优化思路当然就是取消hashmap,也就是说如果不用hashmap,我们该如何通过原节点的random寻找新节点的random呢?
我们可以把新节点都放在对应原始节点的next里面!!!
这样就是和hashmap的key-value一样的道理!!!
只是处理起来复杂一点,具体分三步:
- 把复制的结点链接在原始链表的每一对应结点后面
- 把复制的结点的random指针指向被复制结点的random指针的下一个结点
- 拆分成两个链表,奇数位置为原链表,偶数位置为复制链表,注意复制链表的最后一个结点的next指针不能跟原链表指向同一个空结点None,next指针要重新赋值None(判定程序会认定你没有完成复制)
根据上面三步,代码如下,每个while循环完成一个功能
public class Solution {
public RandomListNode Clone(RandomListNode pHead) {
if(pHead == null) {
return null;
}
RandomListNode currentNode = pHead;
//1、复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
while(currentNode != null){
RandomListNode cloneNode = new RandomListNode(currentNode.label);
RandomListNode nextNode = currentNode.next;
currentNode.next = cloneNode;
cloneNode.next = nextNode;
currentNode = nextNode;
}
currentNode = pHead;
//2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
while(currentNode != null) {
currentNode.next.random = currentNode.random==null?null:currentNode.random.next;
currentNode = currentNode.next.next;
}
//3、拆分链表,将链表拆分为原链表和复制后的链表
currentNode = pHead;
RandomListNode pCloneHead = pHead.next;
while(currentNode != null) {
RandomListNode cloneNode = currentNode.next;
currentNode.next = cloneNode.next;
cloneNode.next = cloneNode.next==null?null:cloneNode.next.next;
currentNode = currentNode.next;
}
return pCloneHead;
}
}