题目描述:
复制一个复杂链表,在复杂链表中,每个结点除了有一个next指针指向下一个结点外,还有一个sbiling指向链表中的任意结点或者null。
下图是一个复杂链表的示例,Null的指针没有画出。
解题思路:
1.很直观的解法就是分成两步:
1).复制原始链表上的每一个结点,并用next指针连起来。
2).复制sbiling指针。
但是复制sbiling指针时需要比较高的复杂度。
以上图为例,如果我们要复制B对应B’的的sbiling指针,那就是要找到E’,想要找到E’只能根据
B到E要走的步数 = B’到E’要走的步数
然而又如D.sbiling = B,指向的结点在它的前面,而链表并没有指向前一个元素的指针,所以,每次都只能根据从链表头结点到目标的结点的步数来找到sbiling应该指向的元素。
这种方法显然效率太低,时间复杂度达到了O(n*n)。
2.书中提到了利用哈希表存储(N, N’)的配对信息的方法
这是一个在时间上很高效的方法,在查找上,利用哈希表的高效性。但是缺点在于要用额外的空间。
上述方法的时间主要花在定位结点的m_pSibling上面,我们试着在这方面做优化。还是分两步:第一步仍然是复制原始链表上的每个结点N,并创建 N’,然后把这些创建出来的结点链接起来。同时我们把<N, N’>的配对信息放到一个哈希表中。第二步还是设置复制后的链表上每个结点的m_pSibling。如果在原始链表中结点N的m_pSibling 指向结点S,那么在复制后的链表中,对应的N’应该指向S’。由于有了哈希表,可以用O(1)的时间根据S找到S’。这种方法相当于用空间换时间,以 O(n)的空间消耗把时间复杂度由O(n^2)降低到O(n)。
(注:哈希表中的每一个配对(pair)的Key是原始链表的结点,Value是Key中 结点的对应的拷贝结点。这样在哈希表中,就可以在O(1)的时间里,找到原始结点对应的拷贝出来的结点。比如想求得N’的m_pSibling所指向的 S’,可以由N的m_pSibling求得S,而<S, S’>的配对信息就在哈希表中,可以用O(1)时间求得。)
3.更为高效的一种不利用辅助空间的方法
这个方法的巧妙之处在于利用链表结点本身记录sbiling指针的位置。
分成三个步骤
1).根据原始链表的每个结点N创建对应的N’,并把N’连在N的后面。
如下图:
2)看到上图我们就应该知道这个算法的巧妙之处了,B’.sbiling就记录在B.sbiling.next,这一步就是通过这个方法设置sbiling指针了。
3).将两个链表断开。
package cglib;
class ComplexListNode
{
int data;
ComplexListNode next;
ComplexListNode sibling;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("data = " + data);
sb.append(", next = " + (next == null ? "null" : next.data));
sb.append(", sbiling = " + (sibling == null ? "null" : sibling.data));
return sb.toString();
}
}
public class DeleteNode{
public static void copyList(ComplexListNode head){
ComplexListNode node = head;
while(node != null){
ComplexListNode copyNode = new ComplexListNode();
copyNode.data = node.data;//1的数据给01
copyNode.next = node.next;//1指向2的指针给01,相当于01指向2
copyNode.sibling = null;
//1->2->3,变成1->01->2->02->3->03
node.next = copyNode;//1指向01
node = copyNode.next;//然后以2开始又继续下一个while循环
}
}
//1->01->2->02->3->03,1--->3,01--->03
public static void setSbiling(ComplexListNode head){
ComplexListNode node = head;
// 当前处理的结点sibling字段不为空,则要设置其复制结点的sibling字段
while(node != null){
ComplexListNode copyNode = node.next;//01开始
if(node.sibling != null){//1指向3
copyNode.sibling = node.sibling.next;//01指向03,相当于1指向3,3的下一个结点就是03
}
node = copyNode.next;
}
}
//1->01->2->02->3->03,1--->3,01--->03
public static ComplexListNode disConnectList(ComplexListNode head){
ComplexListNode node = head;
ComplexListNode copyHead = null;
ComplexListNode copyNode = null;
if(node != null){// node 不为空才能进行下面的while循环
copyHead = node.next;//01为头
copyNode = node.next;
node.next = copyNode.next;//1的下一个01变成01的下一个2
node = node.next;//然后将2作为下一个结点进入下面的while循环
}
while(node != null){
copyNode.next = node.next;//01的下一个就是02, //把偶数位置的结点链接起来就是复制出来的新链表
copyNode = copyNode.next;//以02开始进行下一个, //把奇数位置的结点链接起来就是原始链表
node.next = copyNode.next;//2的下一个就是3,相当于02的下一个
node = node.next;//以3作为结点进行下一个
}
return copyHead;
}
public static ComplexListNode copy(ComplexListNode head){
copyList(head);
setSbiling(head);
return disConnectList(head);
}
public static void main(String[] args) {
ComplexListNode head = new ComplexListNode();
head.data = 1;
ComplexListNode node2 = new ComplexListNode();
node2.data = 2;
ComplexListNode node3 = new ComplexListNode();
node3.data = 3;
ComplexListNode node4 = new ComplexListNode();
node4.data = 4;
ComplexListNode node5 = new ComplexListNode();
node5.data = 5;
head.next = node2;
head.sibling = node3;
node2.next = node3;
node2.sibling = node5;
node3.next = node4;
node4.next = node5;
node4.sibling = node2;
ComplexListNode copyHead = copy(head);
ComplexListNode node = copyHead;
while(node != null){
System.out.println(node);
node = node.next;
}
}
}
输出:
data = 1, next = 2, sbiling = 3
data = 2, next = 3, sbiling = 5
data = 3, next = 4, sbiling = null
data = 4, next = 5, sbiling = 2
data = 5, next = null, sbiling = null
下面是map实现:
package cglib;
import java.util.HashMap;
class RandomListNode
{
int data;
RandomListNode next = null;
RandomListNode sibling = null;
RandomListNode(int data) {
this.data = data;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("data = " + data);
sb.append(", next = " + (next == null ? "null" : next.data));
sb.append(", sbiling = " + (sibling == null ? "null" : sibling.data));
return sb.toString();
}
}
public class DeleteNode{
public static RandomListNode Clone(RandomListNode pHead)
{
if(pHead == null)
return null;
HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
RandomListNode newHead = new RandomListNode(pHead.data);
RandomListNode pre = pHead, newPre = newHead;//pre用于扫描原链表
//newHead指向复制链表,newPre用于扫描复制链表
map.put(pre, newPre);
while(pre.next != null){
newPre.next = new RandomListNode(pre.next.data);
pre = pre.next;
newPre = newPre.next;
map.put(pre, newPre);
}
pre = pHead;
newPre = newHead;
while(newPre != null){
newPre.sibling = map.get(pre.sibling);
pre = pre.next;
newPre = newPre.next;
}
return newHead;
}
public static void main(String[] args) {
RandomListNode head = new RandomListNode(1);
RandomListNode node2 = new RandomListNode(2);
RandomListNode node3 = new RandomListNode(3);
RandomListNode node4 = new RandomListNode(4);
RandomListNode node5 = new RandomListNode(5);
head.next = node2;
head.sibling = node3;
node2.next = node3;
node2.sibling = node5;
node3.next = node4;
node4.next = node5;
node4.sibling = node2;
RandomListNode copyHead = Clone(head);
RandomListNode node = copyHead;
while(node != null){
System.out.println(node);
node = node.next;
}
}
}
输出:
data = 1, next = 2, sbiling = 3
data = 2, next = 3, sbiling = 5
data = 3, next = 4, sbiling = null
data = 4, next = 5, sbiling = 2
data = 5, next = null, sbiling = null