请实现函数复制一个复杂链表。在复杂链表中,每个结点除了有一个next引用“指向”下一个结点外,还有一个sibling引用“指向”链表中的任意结点或者null。
复杂链表的图示:
O(n^2)的解法,无法拿下offer
第一反应的解决思路是两步走战略:
- 第一步:先不管sibling引用的问题,先把整个链表中的每一个结点都复制并用next连接起来,这一步还是比较简单的;
- 第二步:每个结点都复制完后就该设置每个结点的sibling了。如果链表中有一个结点N,它对应复制后的结点为N’,N结点的sibling指向结点S,S对应复制后的结点为S’,现在我们要设置N’的sibling,我们通过N结点很容易找到S,但是不能找到S’,所有我们从头开始遍历走k步后找到S结点,那么很容易知道S’结点也是从复制后的链表的头结点走k步。这样我们就找到了S’并能设置好N’的sibling。
对于n个结点的链表,每设置一个结点的sibling就要从头开始经过O(n)步才能找到,所以时间复杂度为O(n^2)。
空间换取时间,O(n)的解法
分析上面那种方法我们会发现大部分的时间都花费在了设置sibling的地方,所以我们想办法简化寻找S’的时间。
我们使用哈希表将<N, N'>
这一配对信息放入表中,有了哈希表我们就可以在O(1)的时间根据S找到S’。
这种方法就是典型的空间换时间的方法,通过O(n)的空间将时间复杂度降到了O(n)。
不使用辅助空间,O(n)的解法
上面一种方法中我们使用了辅助空间建立哈希表,我们再换一种思路在不使用辅助空间的情况下实现时间复杂度为O(n)的复制。
我们要将两步走战略升级成三步走战略:
- 第一步:复制每一个结点,并且将这个结点直接插入到原结点后面;
- 第二步:设置sibling,一句代码搞定:
nodeA.next.sibling = nodeA.sibling.next
;
- 第三步:将原结点和复制后的结点分离,复制链表完成。
复杂链表复杂测试环境代码
第一步代码实现:
//第一步:复制每一个结点,并且将这个结点直接插入到原结点后面
private static void CloneInsertNode(ComplexList list){
ComplexNode current = list.head;
while(current!=null){
ComplexNode cloned = new ComplexNode();
cloned.data = current.data;
cloned.next = current.next;
current.next = cloned;
current = cloned.next;
}
}
第二步代码实现:
//第二步:设置sibling
private static void SetSibling(ComplexList list){
ComplexNode current = list.head;
while(current!=null){
ComplexNode cloned = current.next;
if(current.sibling!=null)
cloned.sibling = current.sibling.next;
current = cloned.next;
}
}
第三步代码实现:
//第三步:分离结点
private static ComplexList SeparateList(ComplexList list){
ComplexNode current = list.head;
ComplexNode newHead = current.next;
ComplexList newList = new ComplexList();
newList.head = newHead;
while(current!=null){
ComplexNode cloned = current.next;
current.next = cloned.next;
current = cloned.next;
if(current==null)
break;
cloned.next = cloned.next.next;
}
return newList;
}
综合测试:
public static void main(String[] args) {
ComplexList list = new ComplexList();
//创建链表
list.insertHead(5);
list.insertHead(4);
list.insertHead(3);
list.insertHead(2);
list.insertHead(1);
//设置sibling
list.getNode(1).sibling = list.getNode(5);//1-->5
list.getNode(2).sibling = list.getNode(4);//2-->4
list.getNode(4).sibling = list.getNode(3);//4-->3
list.getNode(3).sibling = list.getNode(3);//3-->3
list.getNode(5).sibling = list.getNode(1);//5-->1
System.out.print("结点1的sibling:");
list.getNode(1).sibling.displayNode();
System.out.print("\n结点2的sibling:");
list.getNode(2).sibling.displayNode();
System.out.print("\n结点4的sibling:");
list.getNode(4).sibling.displayNode();
System.out.print("\n结点3的sibling:");
list.getNode(3).sibling.displayNode();
System.out.print("\n结点5的sibling:");
list.getNode(5).sibling.displayNode();
System.out.print("\nlist:");
list.displayList();
System.out.println("\n================================");
//创建好复杂链表
ComplexList list2 = CopyComplexList(list);
System.out.print("结点1复制后的sibling:");
list2.getNode(1).sibling.displayNode();
System.out.print("\n结点2复制后的sibling:");
list2.getNode(2).sibling.displayNode();
System.out.print("\n结点4复制后的sibling:");
list2.getNode(4).sibling.displayNode();
System.out.print("\n结点3复制后的sibling:");
list2.getNode(3).sibling.displayNode();
System.out.print("\n结点5复制后的sibling:");
list2.getNode(5).sibling.displayNode();
System.out.print("\nlist2:");
list2.displayList();
}
private static ComplexList CopyComplexList(ComplexList list){
if(list==null||list.head==null){
System.out.println("参数不合法!");
return null;
}
CloneInsertNode(list);
SetSibling(list);
return SeparateList(list);
}