题目:
请实现函数ComplexListNode* Clone(ComplexListNode* pHead)。复制一个复杂链表。
在复杂链表中。每个节点除了一个m_pNext指针指向下一个节点外,另一个m_pSibling指向链表中的随意节点或者NULL,节点的定义例如以下:
package com.aii.algorithm;
public class Node {
int value;
Node next;
Node sibling;
public Node(int value) {
this.value = value;
}
public Node() {
}
}
一.思路:
能够分两步来实现:
1.先复制链表。sibling就用旧链表的,照搬。
2.再复制silbing
怎么复制sibling,这个借助一个HashMap(在O(1)时间内可以找到),将Node与其复制的Node'的映射关系存到HashMap中。
在第二步改动sibling的时候,依据原先sibling的Node去Map中找,找到相应的那个就是他应该指向的那个。
代码实现:
package com.aii.algorithm;
import java.util.HashMap;
import java.util.Map;
public class CloneNodes {
// 第一步:复制链表
// 第二步:复制sibing的指针位置
public Node clone(Node head) {
Map<Node, Node> map = new HashMap<Node, Node>();
// copy linkedlist
Node newHead = cloneNode(head, map);
// copy silbing points
Node tmpNewHead = newHead;
while (tmpNewHead != null) {
// get from mapping
tmpNewHead.sibling = map.get(tmpNewHead.sibling);
tmpNewHead = tmpNewHead.next;
}
return newHead;
}
// 1.递归克隆
// 2.同一时候将Node,以及Node'的映射关系放入到map中,方便map.next查找
private Node cloneNode(Node head, Map<Node, Node> map) {
if (head == null) {
return null;
}
Node node = new Node();
node.next = cloneNode(head.next, map);
node.sibling = head.sibling;
map.put(head, node);
node.value = head.value;
return node;
}
}
二、
这里借助了HashMap
假设不借助HashMap,又要在O(n)时间完毕?
不使用Map,在改动sibling的时候。麻烦之处在哪?
在于找不到新复制的节点。
下图:
比方说要在A'位置找A'的silbing,那就要A'与A一起往前走,直到某个节点Node==A.sibling这个时候相应的Node'也应该是A'.silbing。
可是这种时间复杂度为O(n*n)
这里有一个好的办法:
分三步走:
1.将新复制的链表穿插到旧的链表中
2.这样子就行依据左右位置的旧的节点找到sibling位置了
3.还原。
代码实现:
package com.aii.algorithm;
public class CloneNodes2 {
// 1.将新的节点插入到链表原始节点的后面
// 2.将新的节点的sibling的指针给赋值。是原先值的next
// 3.再将新的节点链表从中独立出来
public Node clone(Node head) {
Node tmp = head;
// 1.第一步。插入新的节点
while (tmp != null) {
Node next = tmp.next;
Node node = new Node();
node.value = tmp.value;
node.next = tmp.next;
tmp.next = node;
//
tmp = next;
}
// 2.第二步,设置sibling
int count = 0;
tmp = head;
Node nodeNext = null;
while (tmp != null) {
if (count++ % 2 == 0) {
nodeNext = tmp.next;
} else {
tmp.next = nodeNext.next;
}
tmp = tmp.next;
}
// 3.第三步。将新的链表独立出来,而且还原原先的链表
tmp = head;
Node newHead = null;
count = 0;
while (tmp != null) {
tmp.next = tmp.next.next;
newHead = tmp.next;
tmp = tmp.next;
}
return newHead;
}
}
三、測试用例:
因为引用关系比較复杂,toStringeasy引发空指针。这里附上測试用例:
package com.aii.algorithm;
import org.junit.Before;
import org.junit.Test;
public class CloneNodes2Test {
private Node head = new Node('a');
@Before
public void init() {
Node n2 = new Node('b');
Node n3 = new Node('c');
Node n4 = new Node('d');
Node n5 = new Node('e');
head.next = n2;
n2.next = n3;
n3.next = n4;
n4.next = n5;
//
head.sibling = n3;
n2.sibling = n5;
n3.sibling = n2;
}
@Test
public void test() {
printNode(head);
Node node = new CloneNodes().clone(head);
System.out.println("-----");
printNode(node);
System.out.println("-----");
printNode(head);
}
private void printNode(Node node) {
Node tmp = node;
while (tmp != null) {
int sibling = -1;
if (tmp.sibling != null) {
sibling = tmp.sibling.value;
}
int next = -1;
if (tmp.next != null) {
next = tmp.next.value;
}
System.out.print("value:" + (char) tmp.value);
System.out.print(" sibling:" + (char) sibling);
System.out.println(" next:" + (char) next);
tmp = tmp.next;
}
}
}
结果:
value:a sibling:cnext:b
value:b sibling:enext:c
value:c sibling:bnext:d
value:d sibling:?next:e
value:e sibling:?next:?
-----
value:a sibling:cnext:b
value:b sibling:enext:c
value:c sibling:bnext:d
value:d sibling:?next:e
value:e sibling:?next:?
-----
value:a sibling:cnext:b
value:b sibling:enext:c
value:c sibling:bnext:d
value:d sibling:?next:e
value:e sibling:?
next:?