前言
本博文部分图片, 思路来自于剑指offer 或者编程珠玑
问题描述
思路
对于这个问题, 书中给出了两种解法, 难点在于对于sibling引用的复制
对于 这两种思路, 我本来是想画图的, 但是 感觉绘制起来挺有难度的, so 文字描述吧, “剑指offer” p148 有详细的解释
思路一 : 然后根据原链表的数据创建一个新的链表, 确保该链表中除了next, sibling引用之外的其他卫星数据和原链表中的数据一致, 使用一个Map维护创建的新旧对象的映射< oldObj_N, newObj_N >, 然后在根据原链表的各个结点的next, sibling引用 配合Map更新复制的链表的各个结点的next, sibling引用到各个新的对应的数据
思路二 : 在原来的链表的每一个结点创建其对应的拷贝结点, 并更新旧结点和新节点的引用, 形成”旧-旧 -> 旧-新-旧-新” 使得新旧两个链表练成一个链表, 然后更新每一个新节点的sibling结点为对应的旧结点的sibling结点的下一个结点, 最后拆分两个链表, 即将新链表 和旧链表的各个结点的各个next引用更新为各自应该的下一个结点
参考代码
/**
* file name : Test09ComplexListCopy.java
* created at : 4:22:52 PM Jun 7, 2015
* created by 970655147
*/
package com.hx.test05;
public class Test09ComplexListCopy {
// 复制一个复杂的链表
public static void main(String []args) {
int num = 5;
Node first = createComplexList(num);
Node tmp = first;
while(tmp != null) {
Log.log(tmp);
tmp = tmp.next;
}
Log.horizon();
tmp = complexListCopy(first);
// tmp = complexListCopy02(first);
while(tmp != null) {
Log.log(tmp);
tmp = tmp.next;
}
}
// 思路 : 先复制first以及其之后的所有Node的数据, 在更新每一个node的next, sibling数据
// 更新sibling数据使用一个Map, 其在复制所有Node数据的时候构建, 其维护的是原来的Node 和新的Node的映射
static Node lastFirst = null;
static Node lastHead = null;
static Map<Node, Node> nodesMap = new HashMap<Node, Node>();
public static Node complexListCopy(Node first) {
if(first == null) {
return null;
}
if(first == lastFirst) {
return lastHead;
}
nodesMap.clear();
Node head = new Node(first);
nodesMap.put(first, head);
Node tmp = first.next;
while(tmp != null) {
nodesMap.put(tmp, new Node(tmp));
tmp = tmp.next;
}
for(Node node : nodesMap.values() ) {
node.next = nodesMap.get(node.next);
node.sibling = nodesMap.get(node.sibling);
}
lastFirst = first;
lastHead = head;
return head;
}
// 思路 : 在原来的链表中每一个元素之后插入原来的元素的副本
// 然后 跟新每一个副本元素的sibling
// 然后 差分原来的链表 和副本链表
public static Node complexListCopy02(Node first) {
// 复制原来的链表的每一个元素, 并将其插入到改元素之后
Node tmp = first;
while(tmp != null) {
Node newNode = new Node(tmp);
tmp.next = newNode;
tmp = newNode.next;
}
// 更新副本结点的sibling
tmp = first;
while(tmp != null) {
tmp.next.sibling = tmp.sibling.next;
tmp = tmp.next.next;
}
// 差分链表
Node head = first.next;
tmp = first;
Node tmp02 = tmp.next.next;
// 注意更新 next的顺序, 不要先更新了tmp.next....
while(tmp02 != null) {
tmp.next.next = tmp02.next;
tmp.next = tmp02;
tmp = tmp02;
tmp02 = tmp.next.next;
}
tmp.next = tmp.next.next = null;
return head;
}
// 创建一个复杂链表, sibling 随机指向一个结点
static Random rand = new Random();
private static Node createComplexList(int num) {
Node[] nodes = new Node[num];
nodes[nodes.length-1] = new Node(num-1, null, null);
for(int i=nodes.length-2; i>=0; i--) {
nodes[i] = new Node(i, nodes[i + 1], null );
}
for(int i=0; i<nodes.length; i++) {
nodes[i].sibling = nodes[rand.nextInt(num)];
}
return nodes[0];
}
// 一个结点
static class Node {
// 数据, 下一个结点, 兄弟结点
int data;
Node next;
Node sibling;
// 初始化
public Node() {
super();
}
public Node(int data, Node next, Node sibling) {
super();
set(data, next, sibling);
}
public Node(Node node) {
set(node.data, node.next, node.sibling);
}
// setter
public void set(int data, Node next, Node sibling) {
this.data = data;
this.next = next;
this.sibling = sibling;
}
// Debug
public String toString() {
String nextData = "null";
if(next != null) {
nextData = next.data + "@" + next.hashCode();
}
String siblingData = "null";
if(siblingData != null) {
siblingData = sibling.data + "@" + sibling.hashCode();
}
return data + " - " + nextData + " - " + siblingData;
}
}
}
效果截图
总结
对于第一种思路, 想到不是很难, 第二种思路则是非常巧妙的, 如果不从书籍 或者其他的途径中获取, 估计没有灵感的话, 想破头都不一定能想出来。。
注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!