题目:有一个特殊的链表,其中每个节点不但有指向下一个节点的指针pNext,还有一个指向链表中任意节点的指针pRand(可能为空,也有可能为自身),如何拷贝这个特殊链表?
思路:
拷贝pNext指针非常容易,所以题目的难点是如何拷贝pRand指针。
假设原来链表为A1 -> A2 ->... -> An,新拷贝链表是B1 -> B2 ->...-> Bn。
为了能够快速的找到pRand指向的节点,并把对应的关系拷贝到B中。我们可以将两个链表合并成
A1 -> B1 -> A2 -> B2 -> ... -> An -> Bn。
从A1节点出发,很容易找到A1的pRand指向的节点Ax,然后也就找到了Bx,将B1的pRand指向Bx也就完成了B1节点pRand的拷贝。依次类推。
当所有节点的pRand都拷贝完成后,再将合并链表分成两个链表就可以了。
这个题目有个很巧妙的解法,可以达到O(n)的效率,其中心思想是把原始链表和复制链表先合并为一个有固定顺序的链表,然后给复制链表中每个节点的随机指针复制,最后再打断链表恢复原样。
代码如下:
typedef struct __Node { int nData; __Node* pNext; __Node* pRandom; } Node; Node* DuplicateList(Node* pSrcListHead) { if (pSrcListHead == NULL) return NULL; Node* pNode = pSrcListHead; while (pNode != NULL) { Node* pNewNode = new Node; pNewNode->nData = pNode->nData; pNewNode->pNext = pNode->pNext; //pNewNode->pRandom = pNode->pRandom; pNode->pNext = pNewNode; pNode = pNewNode->pNext; } Node* pDestListHead = pSrcListHead->pNext; pNode = pSrcListHead; while (pNode != NULL) { pNode->pNext->pRandom = pNode->pRandom->pNext; pNode = pNode->pNext->pNext; } pNode = pSrcListHead; Node* pNode2 = pNode->pNext; while (pNode != NULL) { pNode->pNext = pNode2->pNext; pNode = pNode->pNext; if (pNode) pNode2->pNext = pNode->pNext; else pNode2->pNext = NULL; pNode2 = pNode2->pNext; } return pDestListHead; }
不过,目前还未考虑到原始链表有环的情况,如果原始链表有环,则应该先求出环的入口点,然后利用上面的方法进行链表复制。