复杂链表的复制
文章目录
- 复杂链表的复制
- 方法一 回溯+哈希表
- 第二种解释
- 方法二:拼接+拆分
- 算法流程
- 参考文献
本题要求我们对一个复杂链表进行复制。在复杂链表中,每个节点除了有一个next指针指向下一个节点,还有一个random指针指向链表中的任意节点或者null.
例1 下图是一个带有随机指针的复杂链表。用一个长度为2的列表表示每个节点,第一个元素表示当前节点的val, 第二个元素表示random指针指向的元素的索引(如果指向null, 则记为null)。则下列复杂链表可以记为:
[[7, null], [13, 0], [11, 4], [10, 2], [1, 0]]
需要对复杂链表进行复制,输出和输入需要相同,仍然为:
[[7, null], [13, 0], [11, 4], [10, 2], [1, 0]]
方法一 回溯+哈希表
本题要求我们对一个特殊的链表进行深拷贝。如果是普通链表,我们可以直接按照遍历的顺序创建链表节点。而本题中因为随机指针的存在,当我们拷贝节点时,当前节点的随机指针指向的节点可能还没创建,因此我们需要变换思路。
一个可行方案是,我们利用回溯的方式,让每个节点的拷贝操作相互独立。对于当前节点,我们首先要进行拷贝,然后我们进行当前节点的后继节点和当前节点的随机指针指向的节点拷贝,拷贝完成后将创建的新节点的指针返回,即可完成当前节点的两指针的赋值。
具体地,我们用哈希表记录每一个节点对应新节点的创建情况。遍历该链表的过程中,我们检查当前节点的后继节点和当前节点的随机指针指向的节点的创建情况。如果这两个节点的任何一个节点的新节点没有被创建,我们都立刻递归地进行创建。当我们拷贝完成,回溯到当前层时,我们即可完成当前节点的指针赋值。
注意一个节点可能被多个其他节点指向,因此我们可能递归地多次尝试拷贝某个节点,为了防止重复拷贝,我们需要首先检查当前节点是否被拷贝过,如果已经拷贝过,我们可以直接从哈希表中取出拷贝后的节点的指针并返回即可。
在实际代码中,我们需要特别判断给定节点为空节点的情况。
第二种解释
利用哈希表的查询特点,考虑构建原链表节点和新链表对应节点的键值对映射关系,再遍历构建新链表各个节点的next和random引用指向即可。
算法流程:
1.若头结点head为空节点,则直接返回null
2.初始化:哈希表dic, 节点cur指向头结点
3.复制链表:
- 建立新节点,并向dic添加键值对(原cur节点,新cur节点)
- cur遍历至原链表下一节点。
4.构建新链表的引用指向
- 构建新节点的next和random引用指向。
- cur遍历至原链表下一节点。
方法二:拼接+拆分
考虑构建原节点1->新节点1->原节点2->新节点2->......
的拼接链表,如此便可在访问原节点的random
指向节点的同时找到心对应新节点的random
指向节点。
算法流程
1.复制各节点,构建拼接链表
- 设原链表为node1->node2->…,构建的拼接链表如下所示:
- node1->node1_new -> node2->node2_new -> …
2.构建新链表各个节点的random指向
- 当访问原节点的cur的随机指向节点cur->random时,对应新节点cur.next的随机指向节点为cur->random->next;
3.拆分原/新链表
- 设置pre/cur分别指向原/新链表的头结点,遍历执行pre->next = pre->next->next和cur->next = cur->next->next,将两链表拆分开。
4.返回新链表的头结点res即可。
参考文献
[1] https://leetcode.cn/problems/fu-za-lian-biao-de-fu-zhi-lcof/solution/jian-zhi-offer-35-fu-za-lian-biao-de-fu-zhi-ha-xi-/