剑指offer-26.复杂链表的复制

https://www.nowcoder.com/practice/f836b2c43afc4b35ad6adc41ec941dba?tpId=13&tqId=11178&tPage=2&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking

题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。

复杂链表结点定义如下:

class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

    RandomListNode(int label) {
        this.label = label;
    }
}

题解:

这里写图片描述

解法一:
分成两步:第一步复制原链表上的每一个结点,并用 next 链接起来;第二步是设置每个结点的 random 指针。

设原始链表结点 N 的random 指针指向的结点为 S,S可能位于N的前面,也可能位于N的后面。所以需要从链表头开始查找,那么在复制链表相应的 N’ 结点的random 指针指向的 S’ 结点时,从头结点走相同的距离即可。

对于每个含有 n 个结点的链表,由于定位每个结点的 random 都需要从链表头结点开始经过 O(n)步才能找到,因此这种方法时间复杂度为 O(n^2)

解法二:
以空间换时间

在第一步时,将每个结点与其复制结点的匹配信息存到一个哈希表中,< N,N’ >; 在第二步中,我们根据原链表 N 的 random 指向的结点 S,不用遍历链表查找S在链表中的位置来确定 S‘,只需从 hash 表中 直接获取 S‘ ,即可将 N’ 的random指向 S‘ 。总的时间复杂度为 O(n)。

class Solution {
    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null) {
            return pHead;
        }
        HashMap<RandomListNode,RandomListNode> cache=new HashMap();
        RandomListNode pHeadCopy=new RandomListNode(pHead.label);//保留复制结点头
        RandomListNode pNode = pHead;//指向原结点
        RandomListNode pNodeCopy=pHeadCopy;//指向复制结点
        cache.put(pNode, pNodeCopy);//保存原结点和它复制的映射

        while(pNode.next!=null){
            pNode=pNode.next;
            RandomListNode node=new RandomListNode(pNode.label);
            pNodeCopy.next=node;
            pNodeCopy=pNodeCopy.next;
            cache.put(pNode, pNodeCopy);//保存原结点和它复制的映射
        }
        //关联random指针
        pNode = pHead;//重新指向原链表头
        pNodeCopy=pHeadCopy;//重新指向复制链表头
        while(pNode!=null){
            pNodeCopy.random=cache.get(pNode.random);
            pNode=pNode.next;
            pNodeCopy=pNodeCopy.next;
        }
        return pHeadCopy;
    }
}

解法三:

不用辅助空间,实现 O(n) 的时间效率。

第一步仍然是根据原始链表的每个结点N 创建对应的 N‘ 。这一次把 N‘ 链接在N的后面。

这里写图片描述

第二步设置复制出来的结点的 random。

这里写图片描述

第三步把长链表拆分成两个链表:把奇数位置的结点用 next 链接起来就是原始链表,把偶数位置的结点用 next 链接起来就是复制出来的链表。

这里写图片描述

public class Solution {
    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null) {
            return pHead;
        }
        // 1.将复制结点连到相应结点的后面
        RandomListNode pNode = pHead;// 遍历用的指针
        while (pNode != null) {
            RandomListNode node = new RandomListNode(pNode.label);
            node.next = pNode.next;
            pNode.next = node;
            pNode = node.next;
        }
        // 2.设置random指针
        pNode = pHead;// 重新指向链表头
        while (pNode != null) {
            if(pNode.random!=null){//如果有random
                pNode.next.random = pNode.random.next;// 复制链表的random指针指向,原链表的random指向的结点的下一个结点
            }
            pNode = pNode.next.next;
        }
        // 3.拆分两个链表,奇数位置为原链表,偶数位置为新链表
        pNode = pHead;// 重新指向链表头
        RandomListNode pHeadCopy = pNode.next;//保留复制链表头
        RandomListNode pNodeCopy=pHeadCopy;//指向复制结点
        pNode.next=pNodeCopy.next;
        pNode=pNode.next;
        while(pNode!=null){
            pNodeCopy.next=pNode.next;//pNodeCopy连接下一个偶数结点,并前移
            pNodeCopy=pNodeCopy.next;
            pNode.next=pNodeCopy.next;//pNode 连接下一个奇数结点,并前移
            pNode=pNode.next;
        }
        return pHeadCopy;
    }
}

猜你喜欢

转载自blog.csdn.net/zxm1306192988/article/details/81041589
今日推荐