复杂链表的复制:最直观的想法是,如果没有随机指针,那么直接进行尾插法即可,如果寻找链表的中间位置,那么使用快慢指针其中每次快指针走两步慢指针走一步即可,但是随机指针可能指向空,如何判断是尾指针呢?深拷贝后随机指针与原指针不一致,如何实现映射呢?方法来啦!虽然随机指针可能指向空,但是前半部分原链表各个节点均会出现且不为空,线性遍历判断是否为空是不可能的,所以此处使用一个map映射,这样只会将原链表部分映射一次即可,注意,此处的映射是原链表节点和深拷贝节点的映射,因为原链表的链表指针以及随机指针均有。
RandomListNode* Clone(RandomListNode* pHead) { //创建头结点 RandomListNode *head=new RandomListNode(-1); head->next=nullptr; head->random=nullptr; //尾结点 RandomListNode *tail=head; //当前结点 RandomListNode *cur=pHead; //原链表结点与深拷贝结点的映射 unordered_map<RandomListNode *,RandomListNode *> umap; //当当前结点不为空并且没有映射 创建原链表结点并且实现next while(cur&&umap.find(cur)==umap.end()) { //拷贝结点 RandomListNode *dcopy=new RandomListNode(cur->label); dcopy->next=nullptr; //衔接结点 tail->next=dcopy; //映射结点 umap[cur]=dcopy; //移动结点 tail=tail->next; cur=cur->next; } //根据映射完成原链表节点的random指针 for(auto [key,value]:umap) { value->random=key->random==nullptr?nullptr:umap[key->random]; } return head->next; }
优化:上述方法我们使用了哈希表即使用了辅助空间,那么能不能写出原地实现的方法呢?当然可以!我们首先遍历原链表并创建拷贝链表,将拷贝链表插在对应的原链表之后,得到形如原1-拷1-原2-拷2...的链表,然后通过原链表的随机指针来设置拷贝链表的随机指针,接着将原链表和拷贝链表进行拆分出来,即一个指针指向原链表第一个结点,一个指针指向拷贝链表第一个结点,然后两个指针均每次移动两个位置即可实现。
RandomListNode* Clone(RandomListNode* pHead) { if(!pHead) return NULL; //空 //拷贝结点 RandomListNode *cur=pHead; while(cur) { //创建结点 RandomListNode *Node=new RandomListNode(cur->label); Node->next=NULL; //将拷贝结点插在源结点后面 Node->next=cur->next; cur->next=Node; //更新当前结点为拷贝结点的下一位 cur=Node->next; } //处理拷贝结点的随机指针 RandomListNode *old=pHead,*newn=pHead->next; while(old) { //将原链表的随机指针赋值给拷贝链表随机指针 newn->random=old->random==NULL?NULL:old->random->next; //原链表走两步 if(old->next) old=old->next->next; //拷贝链表走两步 if(newn->next) newn=newn->next->next; } //分离出原链表和拷贝链表 old=pHead; newn=pHead->next; //拷贝链表头结点 RandomListNode *head=newn; while(old) { if(old->next) old->next=old->next->next; if(newn->next) newn->next=newn->next->next; old=old->next; newn=newn->next; } return head; }