美文网首页
复杂链表的复制(题号35)

复杂链表的复制(题号35)

作者: 莺歌燕舞2018 | 来源:发表于2021-10-27 20:41 被阅读0次

    问题描述:

    输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。

    示例:

    输入:{1,2,3,4,5,3,5,#,2,#}

    输出:{1,2,3,4,5,3,5,#,2,#}

    解析:我们将链表分为两段,前半部分{1,2,3,4,5}ListNode,后半部分{3,5,#,2,#}是随机指针域表示。

    以上示例前半部分可以表示链表为的ListNode1->2->3->4->5

    后半部分3,5,#,2,#分别表示为:1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null

    解法:

    1. hash表辅助存储法

      思路:遍历原链表,在遍历的过程中判断原链表的当前节点是否存在复制节点,存在将复制链表的前序节点next指针指向该复制的节点(从hash表取出),不存在则以当前节点的内容创建新的复制节点,复制链表的前序节点的next指针指向它,并将节点地址映射关系存储进hash表。其次判断原链表的当前节点的random指针指向的节点是否存在复制节点,存在将复制链表的前序节点的next节点的random指针指向该复制的节点(从hash表取出),不存在则以当前节点的random节点内容创建新的复制节点,复制链表的前序节点的next节点的random指针指向它,并将节点地址映射关系存储进hash表。原链表的当前节点curr和复制链表的前序节点prev步进,循环以上的操作直到currnull

      时间复杂度O(n), 遍历一次原链表的时间
      空间复杂度O(n), 哈希表使用的空间

      注意事项:

      1. 题意为深拷贝,所以复制链表的节点不能有指向原链表同位置节点的指针
      2. 特例情况的处理,这里要为复制链表创建虚拟头结点好为前序节点prev初始赋值
    2. 链表拼接、拆分

      思路:这种方法需要遍历三次链表。定义一个游标指针curr,指向原链表的头结点,第一次遍历原链表,遍历的过程中将经过的节点复制并链接到被复制节点的后面:原节点->复制节点->原节点的后续节点curr指针步进,待遍历完整个链表,此时链表的节点数翻倍,一前一后包含原节点和复制出的新节点。将curr指针复位到头结点,第二次遍历更新过后的链表,这次是将复制节点的random指针赋值,判断当前节点的random节点是否为空,如果不为空则将当前节点的后续节点的random指针指向为当前节点的random节点的后续节点。来到第三次遍历,此时跟第二次遍历一样,定义三个指针p1p2和复制出的链表的头结点指针newHeadp1指向当前链表的头节点,p2newHead指向头结点的后续节点,p1p2的后续节点指向为它们后续节点的后续节点,完后分别步进到新的后续节点,当p2的后续节点不为空的时候如此往复。待遍历完后将p1的后续节点指向空(null)完成拆分,返回复制链表的头结点newHead

      时间复杂度O(n), 遍历了三次链表
      空间复杂度O(1), 使用了常数个链表指针变量

      注意事项:

      1. 第三次遍历完后注意将链接原链表的游标指针指向null

    解法一

    图示:

    image

    代码

    public class Solution {
    
        public RandomListNode Clone(RandomListNode pHead) {
            // 原链表的当前节点初始赋值
            RandomListNode curr = pHead;
            // 伪造一个头节点,避免处理特例情况
            RandomListNode dummyHead = new RandomListNode(Integer.MIN_VALUE);
            // 前序节点初始赋值
            RandomListNode prev = dummyHead;
            // 创建一个hash表,用于存储原节点和复制节点的映射关系,例如A->A'
            Map<RandomListNode, RandomListNode> map = new HashMap<>();
            // 遍历原链表
            while (curr != null) {
                // 如果hash表中不存在原链表当前节点的复制节点则以当前节点的内容创建新的复制节点,复制链表的前序节点的next指针指向它,并将节点地址映射关系存储进hash表
                if (!map.containsKey(curr)) {
                    prev.next = new RandomListNode(curr.val);
                    map.put(curr, prev.next);
                } else {// 如果hash表中存在原链表当前节点的复制节点,则复制链表的前序节点的next指针指向该复制节点
                    prev.next = map.get(curr);
                }
                // 如果hash表中不存在原链表当前节点的random指针指向节点的复制节点则以当前节点的random节点内容创建新的复制节点,复制链表的前序节点的next节点的random指针指向它,并将节点地址映射关系存储进hash表
                if (!map.containsKey(curr.random) && curr.random != null) {
                    prev.next.random = new RandomListNode(curr.random.val);
                    map.put(curr.random, prev.next.random);
                } else {// 如果hash表中存在原链表当前节点的random节点的复制节点,则复制链表的前序节点的next节点的random指针指向该复制节点
                    prev.next.random = map.get(curr.random);
                }
                // 步进原链表的当前节点
                curr = curr.next;
                // 步进复制链表的前序节点
                prev = prev.next;
            }
            // 返回复制链表的真实头结点
            return dummyHead.next;
        }
    }
    

    解法二

    图示

    image

    代码

    /*
    class RandomListNode {
        RandomListNode(int val) {
            this.val = val;
        }
    
        int val;
        RandomListNode next;
        RandomListNode random;
    }
    */
    public class Solution {
    
        public RandomListNode copyRandomList(RandomListNode pHead) {
            if (pHead == null) {
                return null;
            }
            RandomListNode curr = pHead;
            while (curr != null) {
                RandomListNode newNode = new RandomListNode(curr.val);
                newNode.next = curr.next;
                curr.next = newNode;
                curr = newNode.next;
            }
    
            curr = pHead;
            while (curr != null) {
                if (curr.random != null) {
                    curr.next.random = curr.random.next;
                }
                curr = curr.next.next;
            }
    
            RandomListNode newHead = pHead.next;
            RandomListNode p1 = pHead;
            RandomListNode p2 = p1.next;
            while (p2.next != null) {
                p1.next = p1.next.next;
                p2.next = p2.next.next;
                p1 = p1.next;
                p2 = p2.next;
            }
            // 注意断开原链表最末节点和新链表最末节点的连接
            p1.next = null;
            return newHead;
        }
    }
    

    相关文章

      网友评论

          本文标题:复杂链表的复制(题号35)

          本文链接:https://www.haomeiwen.com/subject/oxnpaltx.html