Problem:
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
Return a deep copy of the list.
/**什么意思呢,就是给定了一个链表,要返回这个链表的深度拷贝,就是申请新的空间,制造一份结点关系及结点lable值都一样的链表,同时最终还别破换原来的链表,要不复制也没意义了。
所谓复制,就是给个什么弄个一样的什么就行了,在现实中,我们发现,镜像这个东西,就是一模一样的。所以我们先来尝试下镜像。
一、镜像法:
1)首先不管三七二十一先遍历给定链表的同时复制一份。如
现在这种情形若是没有红色部分代表的random指针信息,就已经完成了,那样这个问题也就没什么意思了,难点就在怎么复制random信息,即令1‘ 的random指针指向3’,2‘的random指针也指向3’。我们知道,要是数组的话这个问题就好解决,只要知道跨度就可以令新的结点也跨过相同的跨度就可以了,但是通过new操作得到的指针就无能为力了,获得的地址很随机,没那种特性。别灰心,想想我们新建的链表的random指针还没用呢,虽然不能一步到位的指向应该指向的结点,但总是可以用的上。
2)我们让它们指向与其成镜像的结点,得到下面的情形。
走到这一步好像还看不到希望,别急,开始不是说到镜像了吗,仔细观察,我们发现,1与1‘,3与3’,对,就是镜像关系,我虽然不知道从1到3的跨度,但我们知道,3的镜像肯定就是1‘的random要指向的结点,也就是3’。那么要是3有个指针指向了3‘就好了,而且这种操作也是可以在复制的时候完成的。考虑到每个结点有两种类型的指针,一个是next,一个是random,那么我们要哪类指针来完成这个指向呢?random肯定不行,因为这样的话,在我们没复制random关系前,原来的关系就被我们破坏了,指没了,那就只能是next了,谁让咱们熟悉它呢。
3)接下来的情形如下:
其实,上一步我们选择next的合理解释是,此时的next已经没有多大的意义了,因为即使1的next指针不指向2我们也可以通过拷贝的链表来找到它,即通过2’的random来找到2。所以,next指针别委屈,是你真的太合适了。感谢天,感谢地,感谢阳光照大地,到了这里问题就基本解决了。
那么1‘的random是谁呢?是1’->random->random->next,同样2‘的random也就是2’->random->random->next。
4)但是先别急,我们现在还不能只顾着修改新建链表的random指针,因为此时我们看到原来链表已经被我们破坏掉了,next指针已经指跑偏了。所以的先通过random把原来链表的next指针复位,这就告诉我们,做人要厚道,总不能过河拆桥,卸磨杀驴。
复位操作:1->next = 1->next->next->random, 2->next = 2->next->next->random。
我们已经分析了random的更改操作1’->random->random->next,同样2‘的random也就是2’->random->random->next。所以这里我们需要这二者同步进行修改。比如上图中,我们先通过next和random的配合,找到2,用q指向它,它就是将要建立的1的next,这时我们就可以修改1‘ 的random,以及1的next。同理指针后移,到了2和2’ 处,首先通过2的next和2‘ 的random来找到2的next节点,用q来指向这个节点,也就是3,然后修改2的next,以及2’ 的random。
最终我们得到:
5)看起来很完美是不,不用借用额外的存储空间,关靠指针的相互利用就可以,但是还是有一种情况我们没有考虑到。
在random指向后面的节点甚至是其自身,上面的分析都没问题,但当random指向前面的节点的时候,就出问题了。
按着上面的分析我们从1开始,1 没问题,2 也没问题,当处理 3 的时候,我们发现指向跑偏了,指向2 了,这是因为我们在处理 3 之前修改了 前面的next 域,而在后面又用到了这个域,当然就不对了,看来这个next不能在处理random的同时修改,要等到所有的random指针操作完成后,在归位原来的next域,但这时next 域就完全的找不到了,所以,我们还的在开始破坏next域之前,保存next关系。
//code
class Solution{ public: RandomListNode *copyRandomList(RandomListNode *head) { if(head == NULL) return NULL; RandomListNode *pold = head; RandomListNode *pnew_head = NULL; RandomListNode *pnew = NULL; vector<RandomListNode*> copy_list; //step 1 & 2 & 3 while (pold != NULL) { copy_list.push_back(pold);//store the next relationship RandomListNode *next_old = pold->next; RandomListNode *qnew = new RandomListNode(pold->label); if(pnew_head == NULL)//first node. { pnew_head = pnew = qnew; pnew->random = pold; pold->next = pnew; } else { pnew->next = qnew; qnew->random = pold; pold->next = qnew; pnew = qnew; } pold = next_old; } // 4 pnew = pnew_head; while (pnew)//link the random pointer. { if(pnew->random && pnew->random->random != NULL) pnew->random = pnew->random->random->next; else pnew->random = NULL; pnew = pnew->next; } //recover the next point of origial vector<RandomListNode*>::iterator iter = copy_list.begin(); while((iter + 1)!= copy_list.end()) { (*iter)->next = (*(iter + 1)); ++iter; } (*iter)->next = NULL;//the last one. return pnew_head; } };
这个题目的程序网上很多,这个解法也很多,但大多说的不是很详细,这个解法的得来过程更是含糊不清,这里说的有点啰嗦,但作者觉得也还算详细,希望对读者有一丝的启发,图画的很草,但总算能看的清楚,这就足够了,有什么问题请留言,欢迎不吝指教。