AcWing 48. 复杂链表的复刻(地址映射+原地复刻)

博客介绍了链表复制的两种方法。一是地址映射法,将原链表结点编号,用编号代替指向关系,建立结点地址到数字的映射,用向量存放新结点;二是原地复刻法,在原链表每个结点后生成复制结点,根据原结点指向关系定位复制结点,最后拆分链表,该方法不消耗多余空间。

1.地址映射

考虑将原链表的各个结点从头到尾依次进行编号。这样结点之间的指向关系就可以用编号来代替。在按一般链表的顺序复制好链表以后,就可以用编号的指向关系快速地定位每个新结点的额外指针的指向。这里为了方便对复制的链表进行编号,可以考虑用向量来依次存放每个新结点。至于原链表的结点与编号的关系可以考虑建立一个从结点地址到数字的映射关系。

具体代码如下:

/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *copyRandomList(ListNode *head) {
        unordered_map<ListNode *, int> node_to_num; //结点地址到编号的映射
        vector<ListNode *> num_to_node; //结点编号到地址的映射
        ListNode *p = head;
        int i = 0;
        while(p){
            node_to_num[p] = i++; //建立结点地址到编号的映射
            num_to_node.push_back(new ListNode(p -> val)); //建立结点编号到地址的映射
            p = p -> next;
        }
        num_to_node.push_back(NULL);
        p = head;
        i = 0;
        while(p){
            num_to_node[i] -> next = num_to_node[i + 1]; //顺序连接各个结点
            if(p -> random){ //若随机指针存在则根据映射关系进行连接
                int pos = node_to_num[p -> random];
                num_to_node[i] -> random = num_to_node[pos];
            }
            p = p -> next;
            i++;
        }
        return num_to_node[0];
    }
};

2.原地复刻

建立地址映射是一个比较容易想到和理解的方法。不过这个方法的缺点也比较明显。那就是要消耗额外的空间。这里原地复刻的方法除了新建结点以外不会消耗多余的空间。

此算法的原理是:对于一个链表A->B->C,将每个结点后都生成一个该结点的复制并按顺序连接。也就是说变成A->A`->B->B`->C->C`。然后根据原链表额外结点的指向关系可以很容易地使其后面的复制结点同样定位到对应的新的复制结点当中。最后将整个链表拆分成两个数据域和指针域相同的两个链表即可。

具体代码如下:

/**
 * Definition for singly-linked list with a random pointer.
 * struct ListNode {
 *     int val;
 *     ListNode *next, *random;
 *     ListNode(int x) : val(x), next(NULL), random(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *copyRandomList(ListNode *head) {
        if(!head) return NULL;
        ListNode *p = head, *q = NULL;
        while(p){ //复制每一个结点并将其连接到原结点之后
            ListNode *clone = new ListNode(p -> val);
            clone -> next = p -> next;
            p -> next = clone;
            p = p -> next -> next;
        }
        p = head, q = head -> next;
        while(1){ //连接random指针
            if(p -> random) q -> random = p -> random -> next;
            p = p -> next -> next;
            if(!p) break;
            q = q -> next -> next;
        }
        ListNode *clone_head = head -> next;
        p = head, q = clone_head;
        while(1){ //拆分两个链表
            p -> next = p -> next -> next;
            p = q -> next;
            if(!q -> next) break;
            q -> next = q -> next -> next;
            if(p) q = p -> next;
        }
        return clone_head;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值