16 复杂链表的复制

前言

本博文部分图片, 思路来自于剑指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, 所以请大家指出!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值