剑指offer35题:复杂链表的复制

这篇博客探讨了如何高效地复制一个包含随机指针的复杂链表。首先介绍了初始的O(n^2)解决方案,然后通过使用空间换时间的思想,利用HashMap将时间复杂度降低到O(n)。最后,为了进一步优化空间复杂度,提出了不使用HashMap,而是通过调整链表结构来实现复制,详细描述了这个过程的三个步骤。

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

题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

首先第一种想法是分两步进行,首先复制原始链表的每个节点,通过.next连接起来,然后设置每个节点的random,复杂的就是这一步,因为无法确定random的位置,所以需要从头节点开始找,对于原始链表的每个节点,找到它的random从头节点走了多少步,那新链表对应节点的random也需要从头节点走多少步。这样就找到了对应的random。这一步每个节点random都需要n步,共n个节点,所以时间复杂度是O(n^2);

由此分析可以看出,我们主要的时间花费在了找random上,所以我们想办法优化random的查找,我们在这里不采用暴力查找法,使用空间换时间的思路。试想一下,如果我们可以根据原始链表节点的random节点直接定位到新链表的random节点,不是就不需要从头查找random节点了么?

所以办法就来了,那就是对原始链表的每个节点,都和新链表的每个节点建立一一对应的关系即可,当然我们想到map最合适,这样就实现了O(1)的时间复杂度。

上代码:

public class Solution {
   public RandomListNode Clone(RandomListNode pHead)
    {
        HashMap<Integer, RandomListNode> map = new HashMap<>();
        Stack<RandomListNode> stack = new Stack<>();
        Stack<Integer> stack_random = new Stack<>();
        RandomListNode nHead = new RandomListNode(-1);
        RandomListNode tail = nHead;
        RandomListNode next = pHead;
        while(next != null){
            tail.next = new RandomListNode(next.label);
            tail = tail.next;
            map.put(tail.label, tail);
            if(next.random != null){
                RandomListNode random = map.get(next.random.label); //看是否已经创建了
                if(random != null){
                    tail.random = random;
                }
                else{
                    stack.push(tail);
                    stack_random.push(next.random.label);
                }
            }
            next = next.next;
        }
        while(!stack.isEmpty()){
            stack.pop().random = map.get(stack_random.pop());
        }
        return nHead.next;
    }
}

上面的代码虽然把时间复杂度降下来了,但是空间复杂度上去了,我们接下来再想想能不能在优化一下空间复杂度呢。优化思路当然就是取消hashmap,也就是说如果不用hashmap,我们该如何通过原节点的random寻找新节点的random呢?

我们可以把新节点都放在对应原始节点的next里面!!!

这样就是和hashmap的key-value一样的道理!!!

只是处理起来复杂一点,具体分三步:

  1. 把复制的结点链接在原始链表的每一对应结点后面
    在这里插入图片描述
  2. 把复制的结点的random指针指向被复制结点的random指针的下一个结点
    在这里插入图片描述
  3. 拆分成两个链表,奇数位置为原链表,偶数位置为复制链表,注意复制链表的最后一个结点的next指针不能跟原链表指向同一个空结点None,next指针要重新赋值None(判定程序会认定你没有完成复制)
    在这里插入图片描述
    根据上面三步,代码如下,每个while循环完成一个功能
public class Solution {
    public RandomListNode Clone(RandomListNode pHead) {
        if(pHead == null) {
            return null;
        }
         
        RandomListNode currentNode = pHead;
        //1、复制每个结点,如复制结点A得到A1,将结点A1插到结点A后面;
        while(currentNode != null){
            RandomListNode cloneNode = new RandomListNode(currentNode.label);
            RandomListNode nextNode = currentNode.next;
            currentNode.next = cloneNode;
            cloneNode.next = nextNode;
            currentNode = nextNode;
        }
         
        currentNode = pHead;
        //2、重新遍历链表,复制老结点的随机指针给新结点,如A1.random = A.random.next;
        while(currentNode != null) {
            currentNode.next.random = currentNode.random==null?null:currentNode.random.next;
            currentNode = currentNode.next.next;
        }
         
        //3、拆分链表,将链表拆分为原链表和复制后的链表
        currentNode = pHead;
        RandomListNode pCloneHead = pHead.next;
        while(currentNode != null) {
            RandomListNode cloneNode = currentNode.next;
            currentNode.next = cloneNode.next;
            cloneNode.next = cloneNode.next==null?null:cloneNode.next.next;
            currentNode = currentNode.next;
        }
         
        return pCloneHead;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值