链表入门(二):
拷贝一个带随机指针的链表。
分析:
1.考虑这个问题之前,先思考如何拷贝一个普通的链表:即只有数据和一个next指针的链表。这其实很简单,只需要创建新节点,让新节点的值与就节点相同即可,再将链表连接起来就大功告成了。
上代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
}Node;
//创建节点
Node* BuyNode(int data) {
Node* node = (Node*)malloc(sizeof(Node));
node->data = data;
node->next = NULL;
return node;
}
void NodePrint(Node* head) {
//先进行判空
Node* cur = head;
if (cur == NULL) {
printf("链表为空!!!\n");
}
while (cur != NULL) {
printf("%d -->", cur->data);
cur = cur->next;
}
}
int main() {
Node* n1 = BuyNode(10);
Node* n2 = BuyNode(20);
Node* n3 = BuyNode(30);
Node* n4 = BuyNode(40);
//连接链表
n1->next = n2;
n2->next = n3;
n3->next = n4;
NodePrint(n1);
//开始拷贝原链表
printf("\n\n拷贝的链表是:\n");
Node* cur = n1;
Node* copy = NULL;
Node* last = NULL;//用来记录新链表的尾结点
while (cur != NULL) {
Node* node = (Node*)malloc(sizeof(Node));
//如果拷贝链表为空
if (copy == NULL) {
node->data = cur->data;
node->next = NULL;
copy = node;
last = node;
}
//拷贝链表非空
else {
node->data = cur->data;
node->next = NULL;
last->next = node;
last = node;
}
cur = cur->next;
}
NodePrint(copy);
system("pause");
return 0;
}
这就是普通链表的拷贝过程。
2。学会拷贝普通链表以后,再来考虑拷贝带随机指针的链表。
首先看带随机指针的链表长什么样:
如图,我们用代码将它们的random的指向写出来,就是这样:
node1->random=node3;
node2->random=node1;
node3->random=node3;
node->4->random=NULL;
分析:
我们是否能像普通链表拷贝那样直接复制结点来连接链表?答案是否定的。
原因:比如:当复制node1的时候node3还没有创建,因此random会指向一段非法的内存空间,我们无法去直接拷贝它。
在这里我们利用他们的next来进行拷贝。方法如下:
三部曲:
1.复制旧链表的结点并将其尾插到旧链表的结点中去。
2.根据旧链表的random值设置新链表的random的值
3.将设置好random后的链表拆分成旧链表和新链表。
来张图瞧瞧:
这就是基本思路,话不多说,上代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
struct Node* random;
}Node;
Node* BuyNode(int data) {
Node* node = (Node*)malloc(sizeof(Node));
node->data = data;
node->random = NULL;
node->next = NULL;
return node;
}
Node* NodeInit(Node* head) {
head = NULL;
return head;
}
Node* CopyNode(Node*head) {
//先复制链表的结点并插入到原节点的后面
Node* cur = head;//用来遍历旧链表的结点
while (cur != NULL) {
Node* node = (Node*)malloc(sizeof(Node));
node->data = cur->data;
node->random = NULL;
node->next = cur->next;
cur->next = node;
cur = node->next;
}
//再根据原节点的random对先结点的random进行修改
cur = head;
while (cur != NULL) {
if (cur->random != NULL) {
cur->next->random = cur->random->next;
}
cur = cur->next->next;
}
//最后将链表拆分成旧链表和新链表,再将新链表的头节点返回即可
//:需要做的就是修改旧链表结点的next和新链表节点的next
//因为最后要返回的是新链表的头节点,所以先将它记录下来
cur = head;
Node* newhead = head->next;
while (cur!=NULL) {
Node* node = cur->next;//用来遍历新链表结点
cur->next = node->next;
if (node->next != NULL) {
node->next = cur->next->next;
}
cur = cur->next;
}
return newhead;
}
void NodePrint(Node* head) {
Node* cur = head;
if (cur == NULL) {
printf("链表为空!!!\n");
}
else {
while (cur != NULL) {
printf("%p : %d (%p)---> ", cur, cur->data, cur->random);
cur = cur->next;
}
}
return;
}
int main() {
Node* n1 = BuyNode(10);
Node* n2 = BuyNode(20);
Node* n3 = BuyNode(30);
Node* n4 = BuyNode(40);
//连接链表
n1->next = n2;
n2->next = n3;
n3->next = n4;
//n4->next = NULL;
//random指针的指向
n1->random = n3;
n2->random = n1;
n3->random = n3;
n4->random = NULL;
NodePrint(n1);
printf("\n\n复制后的新链表:\n\n\n");
Node* newnode = CopyNode(n1);
NodePrint(newnode);
system("pause");
return 0;
}
总结:
复制带随即指针的链表步骤——三部曲:
1.复制旧结点的内容插入到旧结点的后面(并将结点的random置空即为NULL)
2.根据旧结点的random指向来设置新链表的random指向。
3.将链表拆分成旧链表和新链表,拆分后的新链表就是目标链表了。