在面试中,最常见的面试题总绕不过链表,关于链表能考的东西太多,对一个题的思考也能从各个方面展现出现。在《剑指offer》中,复杂链表的复制问题就像是一股清流。
关于链表的复制,大家第一反应都是开辟一个新的空间,然后按照next一直拷贝到结束。如果这个时候,这个链表除了next还含有一个random指针,我们在采用这种复制方法,就得反复去寻找这个自由指针所指向的结点,时间复杂度就会比较高,不能让面试官满意。这个时候,可以考虑一下其他的拷贝方法。
在这里,我能想到的办法是这样的:我们直接在这个链表的每一个结点后面都进行拷贝,然后将这些结点按照现在既有的顺序插入进去。然后在对这个链表进行分离,这样就可以得到新拷贝的链表。过程图如下。
说完了过程,下来我们具体来实现一下。因为这是一个复杂链表,含有自由指针,所以我们拷贝的时候,先不考虑自由指针怎么拷贝,把链表就当成一个简单的链表进行拷贝。这部分比较简单,就不画图说明,直接上代码。
typedef struct ComplexNode //链表的结构
{
DataType _data; // 数据
struct ComplexNode * _next; // 指向下一个节点的指针
struct ComplexNode * _random; // 指向随机节点(可以是链表中的任意节点 or 空)
}ComplexNode;
void CloneNode(ComplexNode* head) //对所有结点进行拷贝
{
ComplexNode* cur = head;
while (cur != NULL)
{
ComplexNode* cloneHead = new ComplexNode(); //先将原来的结点除自由指针都进行拷贝
cloneHead->_data = cur->_data;
cloneHead->_next = cur->_next;
cloneHead->_random = NULL; //不考虑所有的自由指针
cur->_next = cloneHead;
cur = cloneHead->_next;
}
}
好了,整个复杂链表的拷贝,我们已经完成了1/3,下来就应该考虑最困难的部分,如何对自由指针进行拷贝。
我们现在构建了一个链表如上图,1的自由指针指向了3,2的自由指针指向了4,5的自由指针指向空。经过上面的第一个拷贝后,就会变成下面这样。如下图。
这个时候我们就要对自由指针进行拷贝了,我们对原来的链表进行遍历,然后拷贝原有链表的自由指针,接着跳过去我们已经拷贝的新节点。也就是说拷贝1的自由指针后,我们直接去拷贝2的。描述如图。
代码如下。
void CloneRandom(ComplexNode* head) //对自由结点进行拷贝
{
ComplexNode* cur = head;
while (cur != NULL)
{
ComplexNode* cloneHead = cur->_next;
if (cur->_random != NULL)
{
cloneHead->_random = cur->_random->_next;
}
cur = cloneHead->_next;
}
}
最后一步,把两个链表分离出来。这部分比较简单,但是需要注意的是,我们一定要先找出两个链表各自的头。然后分别让它们指向属于自己的部分。
ComplexNode* ConnectList(ComplexNode* head)
{
ComplexNode* cur = head;
ComplexNode* newHead = NULL;
ComplexNode* cloneNext = NULL;
if (cur != NULL) //先找出各自的头
{
newHead = cloneNext = cur->_next;
cur->_next = cloneNext->_next;
cur = cur->_next;
}
while (cur != NULL) //再将各自的结点分别链起来
{
cloneNext->_next = cur->_next;
cloneNext = cloneNext->_next;
cur->_next = cloneNext->_next;
cur = cur->_next;
}
return newHead;
}
总结:遇到一个复杂的问题时,可以考虑各个击破,把一个复杂问题一步一步变简单。