解法1:
使用额外空间
使用哈希表,其key和value都是Node。
- key放原来链表的结点 X ,value放复制之后的结点 X’ 。
- 遍历哈希表,或者原来的链表。
- 找到 X 结点的next结点 Y ,再通过map找到复制之后的结点 Y‘ 。
- 将 X’ 的next指针指向 Y‘ 。
- rand指针也一样操作。
- 重复以上过程,直到全部遍历完毕。
我其实在哈希表中存储的不是结点,而是结点的地址,但是也不差。照样可以。当然你想要使用结点也可以。自己试试吧。
//链表复制
#include <bits/stdc++.h>
using namespace std;
struct Node{
int val;
Node* next;
Node(int value){
val = value;
next = NULL;
}
};
class Solution {
public:
Node* head;
Node* end;
int len;
Solution(){
//咋们将链表设置为有头结点的.就是第一个结点是没有数据的那种
Node* node = new Node(-1);
node->next = NULL;
head = node;
end = node;
len = 0;
}
Node* push(int i){
Node* node = new Node(i);
end->next = node;
end = node;
len++;
}
void show(){
Node* p = head->next;
while(p != NULL){
cout<<p->val<<" ";
p = p->next;
}
cout<<endl;
}
void test(){
Node* p = head->next;
unordered_map<Node*,Node*> hash;
//开始往哈希表里面加key和value
//值得注意的是,此时新的结点里面有数据,但指针不正确(指针指向空)
while(p){
hash[p] = new Node(p->val);
p = p->next;
}
//此时开始拷贝指针了.
p = head->next;
while(p) {
//需要考虑该节点有没有下一个结点
if(p->next){
hash[p]->next = hash[p->next];
}
p = p->next;
}
head->next = hash[head->next];
}
void isSame(){
cout<<"地址: "<<head->next<<endl;
}
};
int main(void){
Solution s;
//输入多少的数据
int n;
cin>>n;
//开始输入数据
while(n--){
int a;
cin>>a;
s.push(a);
}
//输出分界之前的链表
cout<<"输出分界之前的链表"<<endl;
s.show();
s.isSame();
s.test();
cout<<"开始输出分界之后的数据"<<endl;
s.show();
s.isSame();
return 0;
}
进阶版本:
-
在老链表的基础上增加结点到老链表中(创建新节点插入到老链表里面);
-
改写新节点们的random结点,如何改写?
举例,1结点的random结点是3,而1‘ 结点的random却是3结点(1结点的random结点)的next结点。即, 1’ ->random = 1 ->random->next; 因此按照这个写一下,就可以了。
-
提取新节点们。
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
Node* p = head;
if(head == NULL){//去除捣蛋的部分
return NULL;
}
//往老链表中,插入新节点
while(p){
Node* node = new Node(p->val);
Node* lat = p->next;
p->next = node;
node->next = lat;
p = lat;
}
//改写新节点们的random指针
p = head;
Node* newNode;
while(p){
//只要老结点存在,其后面定存在新节点
newNode = p->next;
if(p->random){
//需要考虑老结点的random指针指向NULL,这个时候我们就不需要改了
newNode->random = p->random->next;
}
p = p->next->next;
}
//所有准备已经做好了,开始提取新节点了
p = head;
//这两个指针用来定位新的链表
Node* NH = NULL;
Node* NT = NULL;
//这两个指针用来定位老的链表,用完了请记得还原。否则别人怎么用
Node* OH = p;
Node* OT = p;
while(p){
if(NH == NULL){
//如果是第一个新节点
NH = p->next;
NT = p->next;
}else{
NT->next = p->next;
NT = NT->next;
//用来还原老链表的
OT->next = p;
OT = OT->next;
}
p = p->next->next;
}
//需要改一下老链表的最后一个结点的后继,防止乱指。
OT->next = NULL;
return NH;
}
};