Skip to content

Latest commit

 

History

History
166 lines (133 loc) · 6.17 KB

剑指Offer - 25 - 复杂链表的复制.md

File metadata and controls

166 lines (133 loc) · 6.17 KB

剑指Offer - 25 - 复杂链表的复制

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。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

解析

LeetCode138也是一样的题目。两种思路:

1、思路一-Use HashMap

思路:

  • 从左到右遍历链表,对每个结点都复制生成相应的副本结点,然后将对应的关系(之前的结点和新的副本结点)放入哈希表中;
  • 然后从左到右设置每一个副本结点的nextrandom指针,即找到原先curnextrandom的拷贝(从Map中获取);
  • 最后返回副本结点的头结点(map.get(head))即可;

看一个例子:

例如: 原链表 1->2->3->null,假设 1 rand 指针指向 32rand 指针指向 null3rand指针指向 1。遍历到节点 1 时,可以从 map 中得到节点 1 的副本节点1,节点 1next 指向节点 2,所以从 map 中得到节点 2的副本节点 2,然后令 1’.next=2',副本节点了的 next 指针就设置好了。同时节点 1rand 指向节点 3,所以从map 中得到节点 3 的副本节点 3,然后令 1‘.rand=3',副本节点1rand 指针也设置好了。

import java.util.HashMap;
public class Solution {

    public RandomListNode Clone(RandomListNode pHead) {
        HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
        RandomListNode cur = pHead;
        while (cur != null) {
            map.put(cur, new RandomListNode(cur.label));
            cur = cur.next;
        }
        cur = pHead;
        while (cur != null) {
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }
        return map.get(pHead);
    }
}

这里还有一种思路就是在一开始先拷贝好next的,然后后面再拷贝random的:

import java.util.HashMap;
public class Solution {

    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null)
            return null;
        HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
        RandomListNode copyHead = new RandomListNode(pHead.label);
        map.put(pHead, copyHead);
        RandomListNode cur = pHead, copyCur = copyHead;
        while (cur != null) {
            if (cur.next != null)
                copyCur.next = new RandomListNode(cur.next.label);
            map.put(cur.next, copyCur.next);
            cur = cur.next;
            copyCur = copyCur.next;
        }

        // 后面拷贝random的
        cur = pHead;
        while (cur != null) {
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }
        return copyHead;
    }
}

这种写法前面的拷贝next部分也可以改成递归的:

import java.util.HashMap;
public class Solution {

    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null)
            return null;
        HashMap<RandomListNode, RandomListNode> map = new HashMap<>();
        RandomListNode copyHead = rec(pHead, map);

        RandomListNode cur = pHead;
        while (cur != null) {
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }
        return copyHead;
    }

    // 宏观来看: 就是返回拷贝以node为头结点的链表的拷贝 以及next的拷贝
    private RandomListNode rec(RandomListNode node, HashMap<RandomListNode, RandomListNode> map) {
        if (node == null)
            return null;
        RandomListNode copyNode = new RandomListNode(node.label);
        map.put(node, copyNode);
        copyNode.next = rec(node.next, map);
        return copyNode;
    }
}

2、思路二-O(1)空间

本题最优解法是只用O(1)的空间来解决。

  • 第一个步骤,先从左到右遍历一遍链表,对每个结点cur都复制生成相应的副本结点copy,然后把副本结点copy放在cur和下一个要遍历结点的中间;
  • 再从左到右遍历一遍链表,在遍历时设置每一个结点的副本结点的random指针;
  • 设置完random指针之后,将链表拆成两个链表,返回第二个链表的头部;

例子:

代码:

public class Solution {
    public RandomListNode Clone(RandomListNode pHead) {
        if (pHead == null)
            return null;
        RandomListNode cur = pHead, next;
        //先拷贝一份原来的链表
        while (cur != null) {
            next = cur.next;  //先存着之前的next
            cur.next = new RandomListNode(cur.label);
            cur.next.next = next;
            cur = next;
        }

        //复制结点的random指针
        cur = pHead;
        RandomListNode copyCur = null;
        while (cur != null) {
            next = cur.next.next; //保存原来链表中的下一个
            copyCur = cur.next; //复制链表的cur
            copyCur.random = cur.random != null ? cur.random.next : null;
            cur = next;
        }

        //拆开两个链表
        RandomListNode copyHead = pHead.next;
        cur = pHead;
        while (cur != null) {
            next = cur.next.next;
            copyCur = cur.next;
            cur.next = next;
            copyCur.next = next != null ? next.next : null;
            cur = next;
        }
        return copyHead;
    }
}