题目:
请实现函数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:?