剑指offer之面试题26:复杂链表的复制

本文介绍了一种复杂链表的复制方法,通过不同步骤优化时间复杂度,最终实现了O(n)时间复杂度的高效复制算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点)。

结点的定义已给出
/*
public class RandomListNode {
    int label;
    RandomListNode next = null;
    RandomListNode random = null;

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

举个例子,更容易明白
下图是一个含有5个结点的该类型复杂链表。图中实线箭头表示m_pNext指针,虚线箭头表示m_pSibling指针。为简单起见,指向NULL的指针没有画出
这里写图片描述

思路:不考虑时间复杂度,把复制分成两步,第一步复制结点并用m_pNext链接,第二步设置每个结点的m_pSibling。对每个结点,从头开始遍历原始链表,找到每个结点m_pSibling指向的结点S(记下从头开始遍走过的步数s),则复制链表结点的m_pSibling指向的结点距离头结点也是s步。

根据以上思路,编码

/**
 * 
 */
package com.su.biancheng;

/**
 * @title CloneListO_n2.java
 * @author Shuai
 * @date 2016-4-28下午9:51:12
 */
public class CloneListO_n2 {

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

        RandomListNode(int label) {
            this.label = label;
        }
    }
    //O(N^2)
    public static RandomListNode Clone(RandomListNode pHead){
        if(pHead==null)
            return null;
        RandomListNode pNode=pHead;
        RandomListNode pCloned=new RandomListNode(pNode.label);//create a new list HeadNode
        RandomListNode p1=pNode;
        RandomListNode p2=pCloned;
        while(p1.next!=null){
            p2.next=new RandomListNode(p1.next.label);
            p1=p1.next;
            p2=p2.next; 
        }
        p1=pNode;
        p2=pCloned;
        while(p1!=null){
            if(p1.random!=null){
                //copy m_pSibling ,traverse list from head to tail,find SNode
                RandomListNode temp=pNode;
                RandomListNode temp2=pCloned;
                while(temp!=p1.random&&temp.next!=null){
                    temp=temp.next;
                    temp2=temp2.next;
                }
                p2.random=temp2;
            }
            p1=p1.next;
            p2=p2.next;
        }
        return pCloned;
    }
    public static void main(String[] args){
        RandomListNode pHead = new RandomListNode(1);
        RandomListNode node1 = new RandomListNode(2);
        RandomListNode node2 = new RandomListNode(3);
        RandomListNode node3 = new RandomListNode(4);
        RandomListNode node4 = new RandomListNode(5);

        pHead.next = node1;
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;

        pHead.random = node2;
        node2.random = node4;
        node3.random = node1;
        System.out.println(Clone(pHead).random.label);
    }
}

上面的时间主要花费在找m_pSibling上,在这方面做优化。还是两步:第一步,复制原始链表结点N到新创建结点N’,并将复制后的结点用next链接,同时把<N,N'>用hashMap存放;第二步,设置链表结点的random。这个时候不用每次从头遍历原始链表确定random,可以直接从hashMap中取出random对应的random’。
改进后的时间算法为O(2n),代码如下

/**
 * 
 */
package com.su.biancheng;

import java.util.HashMap;

/**
 * @title CloneListO_2n_map.java
 * @author Shuai
 * @date 2016-4-29下午8:32:59
 */
public class CloneListO_2n_map {
    public static class RandomListNode {
        int label;
        RandomListNode next = null;
        RandomListNode random = null;

        RandomListNode(int label) {
            this.label = label;
        }
    }
    //O(2N)
    public static RandomListNode Clone(RandomListNode pHead){
        if(pHead==null)
            return null;
        HashMap<RandomListNode,RandomListNode> map = new HashMap<RandomListNode,RandomListNode>();
        RandomListNode pNode=pHead;
        RandomListNode pCloned=new RandomListNode(pNode.label);//create a new list HeadNode
        RandomListNode p1=pNode;
        RandomListNode p2=pCloned;
        map.put(p1, p2);
        while(p1.next!=null){
            p2.next=new RandomListNode(p1.next.label);
            map.put(p1.next, p2.next);
            p1=p1.next;
            p2=p2.next; 
        }
        p1=pNode;
        p2=pCloned;
        while(p1!=null){
            if(p1.random!=null){
                //copy m_pSibling ,according to hashMap's key-value ,find m_pSibling' with m_pSibling
                p2.random=map.get(p1.random);
            }
            p1=p1.next;
            p2=p2.next;
        }
        return pCloned;
    }
    public static void main(String[] args){
        RandomListNode pHead = new RandomListNode(1);
        RandomListNode node1 = new RandomListNode(2);
        RandomListNode node2 = new RandomListNode(3);
        RandomListNode node3 = new RandomListNode(4);
        RandomListNode node4 = new RandomListNode(5);

        pHead.next = node1;
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;

        pHead.random = node2;
        node2.random = node4;
        node3.random = node1;
        System.out.println(Clone(pHead).random.label);
    }
}

用hashMap存储,实际上是用空间换时间。如何不利用辅助空间实现O(n)的时间?
第一步:根据原始结点N创建新的结点N’,但是把N’连在N的next,如下图
这里写图片描述

第二步:设置random。N的random指向S,N’的random指向S’,N的next指向N’,则S的next指向S’。总之N’的random指向S的next。如下图
这里写图片描述

第三步,拆分。把以上得到的长链表,拆分成原始链表和复制链表。奇数位置用next连起来是原始链表。偶数位置连起来是复制链表。如下图
这里写图片描述

代码实现如下:

/**
 * 
 */
package com.su.biancheng;

import java.util.HashMap;

import com.su.biancheng.CloneListO_2n_map.RandomListNode;

/**
 * @title CloneListO_2n_1.java
 * @author Shuai
 * @date 2016-4-29下午9:00:34
 */
public class CloneListO_2n_1 {
    public static class RandomListNode {
        int label;
        RandomListNode next = null;
        RandomListNode random = null;

        RandomListNode(int label) {
            this.label = label;
        }
    }
    //O(2N),S(1)
    public static RandomListNode Clone(RandomListNode pHead){
        if(pHead==null)
            return null;
        RandomListNode pNode=pHead;
        RandomListNode p1=pNode;
        //1 copy per node to node.next,such as, pCloned=pNode.next
        while(p1!=null){
            RandomListNode p2=new RandomListNode(p1.label);//create a new Node copy of original node
            p2.next=p1.next;
            p2.random=null;
            //pCloned add to between p1 and p1.next 
            p1.next=p2;
            p1=p2.next;
        }
        //2 traverse list,make N'.random=N.random.next
        p1=pNode;
        while(p1!=null){
            RandomListNode p2=p1.next;
            if(p1.random!=null){
                p2.random=p1.random.next;
            }
            p1=p2.next;
        }
        //3 break up the list,odd position using next is original list,even position using next is copy list
        RandomListNode pCloned=pHead.next;
        p1=pHead;
        while(p1.next!=null){
            RandomListNode temp=p1.next;
            p1.next=temp.next;
            p1=temp;
        }
        return pCloned;
    }
    public static void main(String[] args){
        RandomListNode pHead = new RandomListNode(1);
        RandomListNode node1 = new RandomListNode(2);
        RandomListNode node2 = new RandomListNode(3);
        RandomListNode node3 = new RandomListNode(4);
        RandomListNode node4 = new RandomListNode(5);

        pHead.next = node1;
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;

        pHead.random = node2;
        node2.random = node4;
        node3.random = node1;
        System.out.println(Clone(pHead).random.label);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值