[算法学习]复杂链表的复制

问题描述: 复制一个复杂链表。 在复杂链表中,每个结点除了有一个指向下一个结点的next指针外,还有一个 sibing指针 指向链表中任意结点或者NULL。

解法一:“爆破”

解法分析:
1. 遍历第一遍数组,把复制链的next和各结点值复制好。需要时间O(n)
2. 第二遍遍历,二重for循环根据原链表,将复制链的slibing指针连接好。需要时间O(n^2)
3. 需要时间复杂度是O(n^2),空间复杂度是O(1)

参考代码如下

/** * 复杂链表的结点 * * @author kesar * */
static class Node
{
    int val;
    Node next;
    Node slibing;

    public Node(){}

    public Node(int val)
    {
        this.val = val;
    }

    @Override
    public String toString()
    {
        return "Node [val=" + val + "]";
    }
}

/** * 第一种解法:暴力复制。复制一遍next的,再每个结点遍历一遍进行slibing的比较。 时间复杂度是:O(n^2),空间复杂度是:O(1) * * @param oHead * @return */
public static Node copy1(Node oHead)
{
    if (oHead == null)
    {
        return null;
    }
    // 复制的链表的头结点
    Node cHead = new Node(oHead.val);

    // oMove是原链表上遍历用的移动指针,cMove是复制链表上遍历用的移动指针
    // 复制Node结点的值以及next链关系
    for (Node oMove = oHead.next, cMove = cHead; oMove != null; cMove = cMove.next, oMove = oMove.next)
    {
        cMove.next = new Node(oMove.val);
    }

    // 复制slibing的链关系
    for (Node oMove = oHead, cMove = cHead; oMove != null; cMove = cMove.next, oMove = oMove.next)
    {
        if (oMove.slibing == null)
            continue;
        // 查找开始: 遍历查找slibing所指原链表结点对应在copy链表中的结点
        Node findNode = cHead;
        for (; findNode != null && findNode.val != oMove.slibing.val; findNode = findNode.next)
            ;
        // 查找结束:findNode就是结果
        cMove.slibing = findNode;
    }

    return cHead;
}

解法二:使用辅助空间

解法分析:
1. 使用一个HashMap(Key:原链表上的结点,Value:复制链表上的结点),空间复杂度是O(n)
2. 遍历第一遍数组:每次创建复制链的结点时,需要先查找HashMap中结点是否存在,不存在才创建,然后保存结点到HashMap中。需要时间O(n)
4. 需要时间复杂度是O(n),空间复杂度是O(n)

参考代码如下

/** * 第二种解法:利用HashMap的O(1)的高效率查找来取代第一中解法中的一个for循环。用O(n)的空间换来O(n)的时间。时间复杂度O(n),空间复杂度:O(n) * * @param oHead * @return */
public static Node copy2(Node oHead)
{
    if (oHead == null)
    {
        return null;
    }
    // 辅助空间:Key:原链表上的结点,Value:复制链表上的结点
    HashMap<Node, Node> map = new HashMap<Node, Node>();

    // 初始化复制链表的头结点
    Node cHead = new Node(oHead.val);
    map.put(oHead, cHead);
    if (oHead.slibing != null)
    {
        cHead.slibing = new Node(oHead.slibing.val);
        map.put(oHead.slibing, cHead.slibing);
    }
    // 作用:遍历原链表,复制结点到复制链表中。
    // 思路是这样:先从map找原结点对应的复制结点,如果没找到,就创建一个,然后连接到复制链表中,然后put入map中,便于其他时候使用
    for (Node oMove = oHead.next, cMove = cHead; oMove != null; oMove = oMove.next, cMove = cMove.next)
    {
        if (map.containsKey(oMove))
        {
            cMove.next = map.get(oMove);
        }
        else
        {
            cMove.next = new Node(oMove.val);
            map.put(oMove, cMove.next);
        }
        Node slibing = oMove.slibing;
        if (slibing == null)
        {
            continue;
        }
        if (map.containsKey(slibing))
        {
            cMove.next.slibing = map.get(slibing);
        }
        else
        {
            cMove.next.slibing = new Node(slibing.val);
            map.put(slibing, cMove.next.slibing);
        }
    }
    return cHead;
}

解法三:“伸长分裂法”

解法分析:
1. 遍历原链表。在原链表的每个结点的后面插入对应复制结点,将原链表“伸长”1倍;
2. 遍历原链表。将复制结点的slibing连接好;
3. 遍历原链表。将原结点和复制结点“分裂”开,将next连接好;
4. 需要时间复杂度是O(n),空间复杂度是O(1)

参考代码如下

/** * @param oHead * @return */
public static Node copy3(Node oHead)
{
    if (oHead == null)
    {
        return null;
    }
    // 1."伸长"
    for (Node oMove = oHead; oMove != null; oMove = oMove.next)
    {
        // 复制结点
        Node copy = new Node(oMove.val);
    // 插入复制结点到原结点的后面
        copy.next = oMove.next;
        oMove.next = copy;
        // 移动到复制结点处
        oMove = copy;
    }
    // 2.连接好slibing
    for (Node oMove = oHead; oMove != null; oMove = oMove.next)
    {
        if (oMove.slibing == null)
        {
            continue;
        }
        Node copy = oMove.next;
        copy.slibing = oMove.slibing.next;
        oMove = copy;
    }
    // 3."分裂",连接好原链表的next和复制链表中的next
    Node cHead = oHead.next;
    oHead.next = cHead.next;
    cHead.next = null;
    for (Node oMove = oHead.next, cMove = cHead; oMove != null; oMove = oMove.next, cMove = cMove.next)
    {
        Node copy = oMove.next;
        oMove.next = copy.next;
        copy.next = null;
        cMove.next = copy;
    }
    return cHead;
}
  • 附:源码地址

你可能感兴趣的:(算法,链表)