LeetCode 面试经典 150_链表_随机链表的复制(59_138_C++_中等)

LeetCode 面试经典 150_链表_随机链表的复制(59_138_C++_中等)

题目描述:

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝 。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。

random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。

你的代码 接受原链表的头节点 head 作为传入参数。

输入输出样例:

示例 1:

输入 :head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出 :[[7,null],[13,0],[11,4],[10,2],[1,0]]
示例 2:

输入 :head = [[1,1],[2,1]]
输出 :[[1,1],[2,1]]
示例 3:

输入 :head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

提示:

0 <= n <= 1000

-104 <= Node.val <= 104

Node.random 为 null 或指向链表中的节点。

题解:

解题思路:

思路一(哈希表):

1、我们可以直接将原链表和复制链表中对应的结点存储在哈希表 中,这样我们就可以直接通过原地址的 next 和 random 来更新复制链表的指向。
例:

2、具体思路如下:

① 创建一个哈希表用于存储两个链表中每个结点地址的对应关系。

② 遍历原链表的同时创建复制链表的 val ,并使用哈希表记录原链表结点地址和复制链表地址的对应关系。

③ 用过哈希表更新复制链表的 next 和 random指向。

3、复杂度分析

① 时间复杂度:O(N),N代表原链表所含结点的数量。创建复制结点的时间复杂度为N,更新结点的指向的时间消耗为N,所以时间复杂度为O(N)。

② 空间复杂度:O(N),,使用了一个额外的哈希表来存储结点地址的对应关系(除存储答案所需空间)。

思路二(迭代 + 节点拆分):

1、我们可以将复制结点创建出来之后直接插入在原结点后边。这样我们在更新random的指向时就可以直接通过原结点random的指向来更新复制结点random的指向。
例:

2、具体思路如下:

① 在每个原结点后边创建一个复制结点并插入,将复制结点的random设置为nullptr。

② 遍历创建后的链表,更新复制结点的random的指向。

③ 将复制结点查下来,将原链表进行复原。

3、复杂度分析

① 时间复杂度:O(N),N代表原链表所含结点的数量。创建复制结点的时间复杂度为N,更新结点的指向的时间消耗为N,所以时间复杂度为O(N)。

② 空间复杂度:O(1),除存储答案所需空间。

代码实现

代码实现(思路一(哈希表)):
cpp 复制代码
class Solution1 {
public:
    // 复制带有随机指针的链表
    Node* copyRandomList(Node* head) {
        // 创建一个unordered_map来存储原链表节点和新链表节点的映射关系
        unordered_map<Node*, Node*> map;
        
        // 第一步:遍历原链表,为每个节点创建一个新的节点
        Node *curNode = head;
        while (curNode != nullptr) {
            // 为当前节点创建一个新的节点,并将原节点和新节点的映射添加到map中
            Node *newNode = new Node(curNode->val);
            map[curNode] = newNode;
            // 移动到下一个节点
            curNode = curNode->next;
        }

        // 第二步:重新遍历原链表,设置新节点的 next 和 random 指针
        curNode = head;
        while (curNode != nullptr) {
            // 为新节点设置 next 和 random 指针,map[curNode->next] 和 map[curNode->random] 通过原节点找到对应的新节点
            map[curNode]->next = map[curNode->next];  // 设置新节点的 next 指针
            map[curNode]->random = map[curNode->random];  // 设置新节点的 random 指针
            // 移动到下一个节点
            curNode = curNode->next;
        }
        
        // 返回新链表的头节点
        return map[head];
    }
};
代码实现(思路二(迭代 + 节点拆分)):
cpp 复制代码
class Solution2 {
public:
    Node* copyRandomList(Node* head) {
        if (head == nullptr) {
            return nullptr;
        }
        Node* head1 = head;
        // 创建复制的结点插入原链表
        while (head1 != nullptr) {
            Node* newNode = new Node(head1->val);
            newNode->next = head1->next;
            head1->next = newNode;
            head1 = head1->next->next;
        }
        //更新head1指向首节点
        head1 = head;

        // 修改新创建结点random的指向
        while (head1 != nullptr) {
            if (head1->random != nullptr) {
                head1->next->random = head1->random->next;
            }
            head1 = head1->next->next;
        }
        
        //将链表一和链表二进行拆分,不使用新的头节点 
        head1 = head;
        Node* head2 = head->next;
        Node* tmp_head2 = head->next;
        while (head2->next != nullptr) {
            head1->next = head1->next->next;
            head2->next = head1->next->next;
            head1 = head1->next;
            head2 = head2->next;
        }
        head1->next = nullptr;
        return tmp_head2;
		
		//将链表一和链表二进行拆分
		//使用一个新的头节点来链接拆分的链表二 
	//	Node *dummyNode=new Node(0);
	//	head1=head;
	//	Node *head2=dummyNode;
	//	while(head1!=nullptr){
	//		head2->next=head1->next;
	//		head2=head1->next;
	//		head1->next=head2->next;
	//		head1=head2->next;
	//	}
	//	head2=dummyNode->next;
	//	delete dummyNode;
	//	return head2;
    }
};
以思路二为例完成代码调试
cpp 复制代码
#include<iostream>
#include<vector>
#include<unordered_map>
using namespace std;


class Node{
public:
	int val;
	Node *next;
	Node *random;
	
	Node(int _val){
		val=_val;
		next=NULL;
		random=NULL;
	}
};

/*创建随机链表*/ 
Node *createNode(vector<vector<int>> &arr){
	Node *head=nullptr,*tail=nullptr;
	int n=0;
	//id_adress保存每个结点的序号和id,使其对应 
	unordered_map<int,Node *> id_adress;
	//采用尾插法先创建一个单链表,先使得random=nullptr 
	for(const auto &i:arr){
		if(head==nullptr){
			head=tail=new Node(i[0]);
		}else{
			tail->next=new Node(i[0]);
			tail=tail->next;
		}
		id_adress[n]=tail;
		++n;
	}
	//将random通过adress_id进行赋值,-1代表nullptr 
	tail=head;
	for(const auto &i:arr){
		if(i[1]==-1){
			tail=tail->next;
			continue;
		}
		tail->random=id_adress[i[1]];
		tail=tail->next;
	}
	
	return head;
}

class Solution1 {
public:
    // 复制带有随机指针的链表
    Node* copyRandomList(Node* head) {
        // 创建一个unordered_map来存储原链表节点和新链表节点的映射关系
        unordered_map<Node*, Node*> map;
        
        // 第一步:遍历原链表,为每个节点创建一个新的节点
        Node *curNode = head;
        while (curNode != nullptr) {
            // 为当前节点创建一个新的节点,并将原节点和新节点的映射添加到map中
            Node *newNode = new Node(curNode->val);
            map[curNode] = newNode;
            // 移动到下一个节点
            curNode = curNode->next;
        }

        // 第二步:重新遍历原链表,设置新节点的 next 和 random 指针
        curNode = head;
        while (curNode != nullptr) {
            // 为新节点设置 next 和 random 指针,map[curNode->next] 和 map[curNode->random] 通过原节点找到对应的新节点
            map[curNode]->next = map[curNode->next];  // 设置新节点的 next 指针
            map[curNode]->random = map[curNode->random];  // 设置新节点的 random 指针
            // 移动到下一个节点
            curNode = curNode->next;
        }
        
        // 返回新链表的头节点
        return map[head];
    }
};

int main(){
	vector<vector<int>> a={{7,-1},{13,0},{11,4},{10,2},{1,0}};

	Solution1 s;
	//创建原随机链表
	Node *head =createNode(a);
	//随机链表的复制
	Node *test =s.copyRandomList1(head);
	
	//只输出复制随机链表的random指向
	while(test!=nullptr){
		if(test->random!=nullptr){
			cout<<test->random->val<<"   ";
		}else{
			cout<<"nullptr   ";
		}
		test=test->next;
	}
	cout<<"null"; 

	return 0;
}

LeetCode 面试经典 150_链表_随机链表的复制(59_138)原题链接

欢迎大家和我沟通交流(✿◠‿◠)

相关推荐
我是华为OD~HR~栗栗呀3 小时前
华为od-22届考研-C++面经
java·前端·c++·python·华为od·华为·面试
我是华为OD~HR~栗栗呀3 小时前
华为OD, 测试面经
java·c++·python·华为od·华为·面试
Yupureki5 小时前
从零开始的C++学习生活 12:AVL树全面解析
c语言·数据结构·c++·学习·visual studio
我是华为OD~HR~栗栗呀5 小时前
华为OD-23届-测试面经
java·前端·c++·python·华为od·华为·面试
我是华为OD~HR~栗栗呀5 小时前
华为od面经-23届-Java面经
java·c语言·c++·python·华为od·华为·面试
再睡一夏就好9 小时前
【C++闯关笔记】详解多态
c语言·c++·笔记·学习·语法·1024程序员节
喜欢吃燃面12 小时前
数据结构算法题:list
开发语言·c++·学习·算法·1024程序员节
胡萝卜3.012 小时前
C++ list核心接口与实战技巧
数据结构·c++·list·list使用
。TAT。12 小时前
C++ - 多态
开发语言·c++·学习·1024程序员节