题目:请实现一个函数用于复制一个复杂链表。在复杂链表中,每个结点除了有一个next指针指向下一个结点外,还有一个sibling指向链表中的任意结点或者null。
方法一:时间O(n)。分为三个步骤。
- 在每一个结点后面复制一个只包含next指针不含sibling的相同的结点。一次遍历O(n)
- 复制结点的sibling指针,其中复制的结点指向的复制的结点。一次遍历O(n)
- 分割链表。一次遍历O(n),难点
注意:必须记录root的位置,即复制一个相同的head,最好不要动root,会影响调用。
下面代码每一个函数代表一个步骤。
public ComplexListNode clone(ComplexListNode root) { cloneNodes(root); connectSiblingNodes(root); return reconnectNodes(root); } public void cloneNodes(ComplexListNode root) { ComplexListNode head = root; while (head != null) { ComplexListNode clone = new ComplexListNode(head.val); clone.next = head.next; clone.sibling = null; head.next = clone; head = clone.next; } System.out.println("cloneNode done."); } public void connectSiblingNodes(ComplexListNode root) { ComplexListNode head = root; while (head != null) { if (head.sibling != null) { head.next.sibling = head.sibling.next; } head = head.next.next; } } public ComplexListNode reconnectNodes(ComplexListNode root) { ComplexListNode head = root; ComplexListNode chead = null; ComplexListNode croot = null; if (root != null) { chead = root.next; croot = root.next; head.next = chead.next; head = head.next; } while (head != null) { chead.next = head.next; chead = chead.next; head.next = chead.next; head = head.next; } return croot; }
方法二:
思路:声明一个空链表,第一次遍历填满next指针。第二次遍历填满sibling指针。必须注意的是因为sibling是随意的,比如4->2,所以每一个sibling必须从头开始遍历。时间O(n^2)
public ComplexListNode clone2(ComplexListNode root) { if (root == null) return null; ComplexListNode head = root; ComplexListNode croot = new ComplexListNode(root.val); ComplexListNode chead = croot; // 复制next指针 while (head.next != null) { chead.next = new ComplexListNode(head.next.val); chead = chead.next; head = head.next; } // 复制sibling指针 head = root; chead = croot; while (head != null) { if (head.sibling != null) { // 此处不能直接用val的相等去判断是否是sibling, // 可能链表中的节点存在相等的值 ComplexListNode tmp = root; int k = 0; while (tmp != head.sibling) { k++; tmp = tmp.next; } tmp = croot; while (k > 0) { tmp = tmp.next; k--; } chead.sibling = tmp; } head = head.next; chead = chead.next; } return croot; }
方法三:
思路:用空间替代时间。使用哈希表,记录<head, clonehead>。通过sibling指针查找clonesibling,对sibling指针进行添加。
注意:不能用<head, head.sibling>,会导致时间为O(n)。
public ComplexListNode clone3(ComplexListNode root) { if (root == null) return null; ComplexListNode head = root; ComplexListNode croot = new ComplexListNode(root.val); ComplexListNode chead = croot; HashMap<ComplexListNode, ComplexListNode> map = new HashMap<ComplexListNode, ComplexListNode>(); while (head.next != null) { chead.next = new ComplexListNode(head.next.val); map.put(head, head.sibling); chead = chead.next; head = head.next; } head = root; chead = croot; while (head != null) { if (head.sibling != null) chead.sibling = new ComplexListNode(map.get(head).val); chead = chead.next; head = head.next; } return croot; }