转自<http://blog.sina.com.cn/s/blog_69824c1f0100v4ob.html>
struct node
{
int data;
struct node * next;
struct node * random;
};
本题来源是ms的一道面试题,要求是将一条有特殊结构的链表进行复制,链表的结构如上,除了正常的2个域之外,多了一个random,它代表一个指向链表 上某一任意节点的指针,要求将该链表复制。如图,其中实线表示next域,虚线表示random域。
难点分析:本题最难的部分就在于如何将random域完好无损的完全拷贝复制到新的链表之上,对于普通链表一个while循环拷贝就可以搞定,但是这里似乎不可行。
突破点:如何保存或记录random域可以作为一个思考的参考点,下面提供2种解法:
1. hash缓存: 我们可以定义一个hash表,key为被拷贝链表中Node的地址,value为新的拷贝链表中Node的值。下面将在伪代码中进一步说明
伪代码如下:
struct node * old_list;
struct node * new_list;
hash ht;
while( old_list != NULL )
{
if( ht.get(old_list) == NULL )
// 当前节点没有被复制过,则拷贝节点,同时将原节点的地址作为key,插入hash表
{
new_node = copy(*old_list);
ht.set(old_list, new_node);
}
else // 已经存在则直接将该节点读出
{
new_node = ht.get(old_list);
}
// 并将新复制的节点插入链表
insert(new_list, new_node);
if( ht.get(old_list->random) == NULL )
// 如果ht的random不存在,那么产生一个random node,并放入hash表
{
random_node = copy(*(old_list->random));
ht.set(old_list->ramdon,random_node);
}
else //否则从hash表中读出random
{
random_node = ht.get(old_list->random);
}
// 将random域进行填充
new_node->random = random_node;
}
这样可以看出,因为地址值是唯一存在的,所以Hash函数的设计可以保证每个节点的值都会被一一映射,并且由于每一个节点都只会被复制一次,并且在代码的遍历过程中就已经完全复制好,空间复杂度为O(n),时间复杂度为O(n)
2. 用一个图的方法看的比较清楚,首先在遍历原来链表的过程中,插入新的节点,比如A_N是copy(A_O)的,同时将A_N->next = A_O->next; A_O->next = A_N;通过一个遍历,我们就可以将链表复制成图中的样子。在完成next域的链接后,需要开始做random域的复制。
在实现了next的结构之后,我们需要开始对new_list的random域和next域进行赋值。
那么有: A_N->random = A_O->random->next;
入图所示:A_O->random为C_O,C_O的next为C_N,这样A_N->random = C_N;
对于 A_N->next = A_N->next->next;即B_N;
该算法复杂度为O(N),空间复杂度为O(1),除了新拷贝的空间之外没有额外地址;