Copy List with Random Pointer

https://oj.leetcode.com/problems/copy-list-with-random-pointer/

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list. 

public RandomListNode copyRandomList(RandomListNode head)


这一题,最直观的做法就是HashMap,键放的是原list的元素,值放的是克隆出来的元素。不管是遇到random pointer还是next pointer的时候先看看指向的对象是否存在于HashMap中,如果存在,就直接返回值域的内容。否则就克隆内容并将键值对放进HashMap中。下面给出对应的代码:

    public RandomListNode copyRandomList(RandomListNode head) {
        HashMap<RandomListNode, RandomListNode> orig_to_copy = new HashMap<RandomListNode, RandomListNode>();
        RandomListNode tmp = head, res = null, copy_tmp = null;
        while(tmp != null){
            RandomListNode copy = new RandomListNode(tmp.label);
            orig_to_copy.put(tmp, copy);
            res = res == null ? copy : res;
            if(tmp == head){
                copy_tmp = copy;
            }else{
                copy_tmp.next = copy;
                copy_tmp = copy_tmp.next;
            }
            tmp = tmp.next;
        }
        tmp = head;
        while(tmp != null){
            if(tmp.random != null){
                orig_to_copy.get(tmp).random = orig_to_copy.get(tmp.random);
            }
            tmp = tmp.next;
        }
        return res;
    }

这段代码的逻辑和上述算法有一点点不同,这段代码是将原链表跑两遍,第一遍是纯复制普通链表,第二遍是连接random pointer,本质和我刚才描述的算法没有不同,只是这样处理在第一个while循环里,逻辑会比较简单一些。


但这并不是最优的算法,最优的算法是这样的。

第一步,以正常链表的方式克隆并且以一种合并的状态合成一个链表。合并的方式是这样的,原链表当前节点的下一个插入自己的克隆节点。这样就会构成下面这样的链表

A->A`->B->B`->C->C`....其中X`是X的克隆。

第二步,再以这样的链表复制random pointer,在这种形态里,当前原链表的节点的random pointer指向的对象的下一个对象就是该节点克隆该指向的对象。

也就是假如A指向C,那么A`一定指向C`,也就是C的下一个节点。所以A`的random Pointer也就很好分配了。

第三步,链接完所有克隆的random node之后,进行分拆。将奇数位的node作为原链表,偶数位的node作为克隆链表分拆成两个链表,返回第二个即可。

public RandomListNode copyRandomList(RandomListNode head) {
        // Good solution
        RandomListNode res_head = null, res_tmp = null, tmp = head;
        while(tmp != null){//第一步
            RandomListNode copy = new RandomListNode(tmp.label);
            copy.next = tmp.next;
            tmp.next = copy;
            res_head = res_head == null ? copy : res_head;
            tmp = tmp.next.next;
        }
        tmp = head;
        while(tmp != null){//第二步
            if(tmp.random != null){
                tmp.next.random = tmp.random.next;
            }
            tmp = tmp.next.next;
        }
        tmp = head;
        res_tmp = res_head;
        while(tmp != null){//第三步
            tmp.next = tmp.next.next;
            if(res_tmp.next != null)
                res_tmp.next = res_tmp.next.next;
            tmp = tmp.next;
            res_tmp = res_tmp.next;
        }
        return res_head;
    }


2018-01-20 Updated

给出一个新写的HashMap的答案,感觉更符合上面描写的算法,一个循环即可。而且感觉这两年半算法退步了不少,但是写代码的style是有进步的。

    public RandomListNode copyRandomList(RandomListNode head) {
        HashMap<RandomListNode, RandomListNode> nodeMap = new HashMap<RandomListNode, RandomListNode>();
        RandomListNode newHead = null, travelNode = head, copyTravelNode = null;
        while (travelNode != null) {
            RandomListNode copiedNode = this._getCopiedNode(nodeMap, travelNode);           
            if (travelNode.random != null) {
                RandomListNode copiedRandom = this._getCopiedNode(nodeMap, travelNode.random);
                copiedNode.random = copiedRandom;
            }
            
            if (newHead == null) {
                newHead = copiedNode;
                copyTravelNode = newHead;
            } else {
                copyTravelNode.next = copiedNode;
                copyTravelNode = copyTravelNode.next;
            }
            travelNode = travelNode.next;
        }
        
        return newHead;
    }
    
    private RandomListNode _getCopiedNode(Map<RandomListNode, RandomListNode> nodeMap, RandomListNode origin) {
        if (nodeMap.containsKey(origin)) {
            return nodeMap.get(origin);
        } else {
            RandomListNode copied = new RandomListNode(origin.label);
            nodeMap.put(origin, copied);
            return copied;
        }
    }



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值