1. 题目描述
LeetCode 第 138 题“复制带随机指针的链表”。给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。要求返回这个链表的深拷贝,即新链表中的每个节点都应该是原链表对应节点的全新副本,并且新链表中节点的随机指针和 next 指针都要正确指向新链表中的相应节点。
2. 模式识别
本题属于链表复制问题,同时涉及指针操作和数据结构的复制。由于存在随机指针,不能简单地按顺序复制节点,需要考虑如何正确处理随机指针的指向。
3. 考点分析
- 链表操作:需要熟悉链表的基本操作,如节点的创建、连接等。
- 深拷贝概念:理解深拷贝和浅拷贝的区别,确保复制的链表是一个全新的、独立于原链表的结构。
- 随机指针处理:如何正确地将新链表中节点的随机指针指向对应的新节点。
4. 所有解法
- 哈希表法:使用哈希表来存储原链表节点和新链表节点的映射关系。首先遍历原链表,创建新节点并建立映射,然后再次遍历原链表,根据映射关系设置新链表节点的 next 和随机指针。
- 节点插入法:在原链表的每个节点后面插入一个新节点,新节点的值与原节点相同。然后根据原节点的随机指针设置新节点的随机指针,最后将新链表和原链表分离。
5. 最优解法(节点插入法)的 C 语言代码
#include <stdio.h>
#include <stdlib.h>
// 定义带随机指针的链表节点结构
struct Node {
int val;
struct Node *next;
struct Node *random;
};
// 函数功能:复制带随机指针的链表
// 参数:
// head: 原链表的头指针
// 返回值:新链表的头指针
struct Node* copyRandomList(struct Node* head) {
if (head == NULL) {
return NULL;
}
// 第一步:在原链表每个节点后面插入一个新节点
struct Node* cur = head;
while (cur != NULL) {
// 创建新节点
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->val = cur->val;
// 将新节点插入到原节点后面
newNode->next = cur->next;
cur->next = newNode;
// 移动到下一个原节点
cur = newNode->next;
}
// 第二步:设置新节点的随机指针
cur = head;
while (cur != NULL) {
if (cur->random != NULL) {
// 新节点的随机指针指向原节点随机指针所指节点的下一个节点(即对应的新节点)
cur->next->random = cur->random->next;
} else {
cur->next->random = NULL;
}
// 移动到下一个原节点
cur = cur->next->next;
}
// 第三步:分离原链表和新链表
cur = head;
struct Node* newHead = head->next;
struct Node* newCur = newHead;
while (cur != NULL) {
// 恢复原链表的 next 指针
cur->next = newCur->next;
cur = cur->next;
if (cur != NULL) {
// 设置新链表的 next 指针
newCur->next = cur->next;
newCur = newCur->next;
}
}
// 新链表的最后一个节点的 next 指针置为 NULL
newCur->next = NULL;
return newHead;
}
6. 时间复杂度和空间复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要遍历链表三次,每次遍历的时间复杂度都是 O ( n ) O(n) O(n)。
- 空间复杂度: O ( 1 ) O(1) O(1),只使用了常数级的额外空间,没有使用额外的数据结构来存储映射关系。
以下是一个简单的测试代码示例:
// 辅助函数:创建新节点
struct Node* createNode(int val) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->val = val;
newNode->next = NULL;
newNode->random = NULL;
return newNode;
}
// 辅助函数:打印链表
void printList(struct Node* head) {
struct Node* cur = head;
while (cur != NULL) {
printf("Value: %d, Random Value: ", cur->val);
if (cur->random != NULL) {
printf("%d\n", cur->random->val);
} else {
printf("NULL\n");
}
cur = cur->next;
}
}
int main() {
// 创建测试链表
struct Node* node1 = createNode(1);
struct Node* node2 = createNode(2);
struct Node* node3 = createNode(3);
node1->next = node2;
node2->next = node3;
node1->random = node3;
node2->random = node1;
node3->random = node2;
// 复制链表
struct Node* newHead = copyRandomList(node1);
// 打印新链表
printList(newHead);
return 0;
}
在这个代码中,copyRandomList
函数实现了带随机指针链表的复制。首先在原链表每个节点后面插入新节点,然后设置新节点的随机指针,最后分离原链表和新链表。主函数中创建了一个测试链表,调用 copyRandomList
函数进行复制,并打印新链表。