题目:已知一链表,每个节点除了有一个指向下一节点的指针外,还有一随机指针指向链表中的任意节点(可能为空,也有可能为自身),请复制一个链表,要求节点的顺序以及节点上的随机指针指向的节点位置和原链表一致。
解法一:
空间换时间,利用数组、链表等数据结构存储Random指针位置信息,这里样例代码使用Hash表,这样空间利用率是O(n),时间效率也是O(n)。
- #include <hash_map>
- using namespace std;
- using namespace stdext;
- struct Node
- {
- Node* pNext;
- Node* pRandom;
- };
- Node* Copy(Node* pHead)
- {
- if (pHead = NULL)
- return NULL;
- // 额外存储空间,利用Hash方法达到常数级的查找开销
- hash_map<Node*, int> NodeMap;
- int nIndex = 0;
- Node* pNode = pHead;
- // 将链表存入map中,并记录下编号
- while (pNode != NULL)
- {
- if (NodeMap.insert(hash_map<Node*, int>::value_type(pNode, nIndex)).second)
- {
- nIndex++;
- pNode = pNode->pNext;
- }
- else // 到达环的交点
- break;
- }
- Node* pNewNodes = new Node[NodeMap.size()];
- // 顺序复制每个节点的指针
- nIndex = 0;
- for (hash_map<Node*, int>::iterator it = NodeMap.begin(); it != NodeMap.end(); it++, nIndex++)
- {
- int nNext = 0, nRandom = 0;
- Node* pOriginal = it->first;
- // 复制Next
- if (pOriginal->pNext != NULL)
- {
- hash_map<Node*, int>::iterator temp = NodeMap.find(pOriginal->pNext);
- pNewNodes[nIndex].pNext = &pNewNodes[temp->second];
- }
- else
- pNewNodes[nIndex].pNext = NULL;
- // 复制Random
- if (pOriginal->pRandom != NULL)
- {
- hash_map<Node*, int>::iterator temp = NodeMap.find(pOriginal->pRandom);
- pNewNodes[nIndex].pRandom = &pNewNodes[temp->second];
- }
- else
- pNewNodes[nIndex].pRandom = NULL;
- }
- return pNewNodes;
- }
解法二:
此方法的中心思想是把原始链表和复制链表先合并为一个有固定顺序的链表,然后给复制链表中每个节点的随机指针复制,最后再打断链表恢复原样。和解法一相比,空间效率上更佳。
代码如下:
- 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 (Node)
- pNode2->pNext = pNode->pNext;
- else
- pNode2->pNext = NULL;
- pNode2 = pNode2->pNext;
- }
- return pDestListHead;
- }
不过目前还未考虑到原始链表有环的情况,如果原始链表有环,则应该先求出环的入口点,然后利用上面的方法进行链表复制,在这里就不详细列出代码了。