剑指 Offer 35. 复杂链表的复制

题目

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]
示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。

提示:

-10000 <= Node.val <= 10000
Node.random 为空(null)或指向链表中的节点。
节点数目不超过 1000 。

暴力解法

本题要求的是链表的复制,限制了复制后的结果不能指向原链表,即要求完成的是值的复制,且要求保证原链表不发生变化。
故先将复杂链表复制为数组tmp(此处还是地址的引用),后新建copy数组依次复制tmp中的值,从而实现复杂链表的复制。

class Solution {
    public Node copyRandomList(Node head) {  
        if( head == null )
            return null;
        //因为要求不改变原链表,所以先复制一遍
        ArrayList<Node> tmp = new ArrayList<>();
        while( head != null ) {
            tmp.add(head);
            head = head.next;
        }
        //不能直接return tmp.get(0),因为这样引用的地址还是一样的
        //这里每个值都不能直接引用,只能取它的具体值!!!
        //正式开始复制链表
        ArrayList<Node> copy = new ArrayList<>();
        for( int i = 0; i<tmp.size(); ++i )
            copy.add( new Node(tmp.get(i).val) );
        //复制val值
        for (int i = 0; i < tmp.size(); ++i) {
            //复制random值
            copy.get(i).random 
                = tmp.get(i).random == null?null:copy.get(tmp.indexOf(tmp.get(i).random));
            /*random指向copy.get(x),且copy.get(x) = tmp.get(x) 
            其中 x = indexOf(tmp.get(i).random),*/
            if( i != tmp.size()-1 ) 
                copy.get(i).next = copy.get(i+1);
            //复制next值
        }
        return copy.get(0);
    }
}

官方迭代法

思路:将A’插入A和B之间,并给其赋val,random值,最后将新旧链表分开。

class Solution {
    public Node copyRandomList(Node head) {
        if (head == null) 
            return null;
        /*指向新节点,又回到旧节点,即A → A'→ B → B'→ C
        实际作用大概是复制val*/      
        for (Node node = head; node != null; node = node.next.next) {
            Node nodeNew = new Node(node.val);
            nodeNew.next = node.next;//先连哪个指针是有讲究的捏
            node.next = nodeNew;
        }
        //复制random
        for (Node node = head; node != null; node = node.next.next) {
            Node nodeNew = node.next;//nodeNew即新节点,A',B'等
            nodeNew.random = (node.random != null) ? node.random.next : null;
            //random.next是因为要指的是新节点啦
        }
        //将新旧链表分开
        Node copy = head.next;
        for (Node node = head; node != null; node = node.next) {
            Node nodeNew = node.next;
            node.next = node.next.next;
            nodeNew.next = (nodeNew.next != null) ? nodeNew.next.next : null;
        }
        return copy;
    }
}

哈希表实现

实现思路:运用map实现由原节点到新节点的映射。遍历链表过程中,需要检查「当前节点的后继节点」和「当前节点的随机指针指向的节点」的创建情况。如果这两个节点中的任何一个节点的新节点没有被创建,递归进行创建。

class Solution {
    Map<Node, Node> cachedNode = new HashMap<Node, Node>();

    public Node copyRandomList(Node head) {
        if (head == null) 
            return null;
        if ( !cachedNode.containsKey(head) ) {//若头节点尚未建立映射关系
            Node headNew = new Node(head.val);
            cachedNode.put( head, headNew );
            headNew.next = copyRandomList(head.next);
            headNew.random = copyRandomList(head.random);
        }
        return cachedNode.get(head);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值