copy-list-with-random-pointer

介绍一种高效算法来深拷贝带有随机指针的链表,该算法巧妙利用原有链表信息,实现时间复杂度O(N),空间复杂度O(1)。

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

题目描述


A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.


分析1:

最开始的想法就是暴力复制,时间复杂度为O(n^2),写的时候就感觉要出现事,果不其然,超时了,后来网上看到一个O(n)的算法,非常巧妙的利用了原来链表的信息:

该算法更为巧妙,不用保存原始链表的映射关系,构建新节点时,指针做如下变化,即把新节点插入到相应的旧节点后面:
 
 
同理分两步
 
1、构建新节点random指针:new1->random = old1->random->next, new2-random = NULL, new3-random = NULL, new4->random = old4->random->next
 
2、恢复原始链表以及构建新链表:例如old1->next = old1->next->next,  new1->next = new1->next->next
 
该算法时间复杂度O(N),空间复杂度O(1)
 


分析2:(该方式类似于分析1中的第二个方法)

我们知道如果是简单的copy List 的话,那么我们只需要从头到尾遍历下来,new出对应个数的Node,并把它们的连接关系设置好就可以了,但是这道题目中每个节点Node出现了Random属性,也就意味着可能当前结点Node所依赖的那个Random对应的结点还没有被创建出来。

解题思路:

为了解决“分析”里提到的问题,我们需要做如下三步处理!

1. 在OldList中的每个结点后,插入一个CopyNode,这个结点的Random域和Next域与OldList中的被拷贝Node的Random域和Next一致,然后让被拷贝结点的Next域指向CopyNode结点,这样先创建出OldList中结点对应的CopyNode结点。

2. 由于所有的CopyNode都已经创建出来了,我们就可以调整这些CopyNode真正的Random域的值了。

3. 调整所有CopyNode的Next域的值,恢复OldList所有Node的Next的值到初始状态!

不能把第二步和第三部一起来处理,因为后面的结点可以指到前面,而前面的结点已经断开了,其random指针的next是原来的List,故报错。

图解:

代码:

package com.leetcode;

public class CopyRandomList {
	class RandomListNode {
		int label;
		RandomListNode next, random;

		RandomListNode(int x) {
			this.label = x;
		}
	};

	public RandomListNode copyRandomList(RandomListNode head) {
		if (head == null)
			return null;

		/* 第一步:在OldList的每个节点后面都插入一个copyNode(拷贝链表的结点) */
		RandomListNode nowNode = head;
		while (nowNode != null) {
			RandomListNode copyNode = new RandomListNode(nowNode.label);
			copyNode.random = nowNode.random;
			copyNode.next = nowNode.next;
			nowNode.next = copyNode;
			nowNode = nowNode.next.next;
		}

		/*
		 * 第二步:确定NewList的每个节点,真正关联到的Random结点是哪个, 因为第一步已经把所有NewList上的结点都建立了
		 */
		nowNode = head;
		while (nowNode != null) {
			if (nowNode.random != null)
				nowNode.next.random = nowNode.random.next;
			nowNode = nowNode.next.next;
		}

		/*
		 * 第三步:还原OldList的next为一开始的next结点 并拼接NewList的next到它真正所应该关联的next结点
		 * 即:保持老链表OldList不变,拼接新链表NewList!
		 */
		// 这一部分特别绕,也是难点
		RandomListNode pHead = new RandomListNode(0);
		pHead.next = head;
		RandomListNode newlist = pHead;

		nowNode = head;
		while (nowNode != null) {
			pHead.next = nowNode.next;
			nowNode.next = pHead.next.next;
			pHead = pHead.next;
			nowNode = nowNode.next;
		}

		return newlist.next;
	}
}


暴力法代码:
/**
 * Definition for singly-linked list with a random pointer.
 * struct RandomListNode {
 *     int label;
 *     RandomListNode *next, *random;
 *     RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    RandomListNode *copyRandomList(RandomListNode *head) {
        if (head == NULL) return NULL;
        RandomListNode *res = new RandomListNode(head->label);
        RandomListNode *pos1 = head->next, *pos2 = res;
        while (pos1 != NULL) {
            pos2->next = new RandomListNode(pos1->label);
            pos1 = pos1->next;
            pos2 = pos2->next;
        }
        pos1 = head;  pos2 = res;
        RandomListNode *idx1 = head, *idx2 = res;
        while (pos1 != NULL) {
            if (pos1->random == NULL) {
                pos2->random = NULL;
            } else {
                idx1 = head; idx2 = res;
                while (pos1->random != idx1) {
                    idx1 = idx1->next;
                    idx2 = idx2->next;
                }
                pos2->random = idx2;
            }
            pos1 = pos1->next;
            pos2 = pos2->next;
        }
        return res;
    }
};



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值