题目描述: 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个next指向下一个节点,另一个特殊指针random指向一个随机节点), 请对此链表进行深拷贝,并返回拷贝后的头结点。
最近有人跟我提到这个问题,网上一搜原来是一个经典面试题。我的笨脑袋想不到最优解法,但是想到了两个常规解和一个带限制的解法。下面把这四种解法都列出来。
解法一:
先做正常拷贝,即next正确。然后同时遍历两个链表,对于原链表的每个结点,再同时遍历两个链表找到原random所在位置,也就有了新random所在位置。
时间复杂度为O(n^2)。
解法二:
先做正常拷贝,即next正确,同时建立一个哈希表<原结点,复制结点>。同时遍历两个链表,对于每一个复制结点,其random等于对应的原结点的random,在表中的映射。
时间复杂度大致为O(n),因为哈希表通常很快。空间复杂度稍高。但是对于实际工程,这个哈希表通常可以复用,而且实现较有条理,容易理解,因此我认为在实际项目中这是最合适的解法。
解法三:
先做正常拷贝,即next正确,对于每个原结点,将其random指向对应的复制结点,而复制结点的random指向原结点的random。相当于是让复制结点充当一个桥接,复用原内存建立两个映射,并且没有丢失信息。
最后同时遍历两个链表,每一步都重建原结点和复制结点的random。
但是这个方法有个限制,必须前提保证random向后指,不能向前指,否则遍历的过程中也同时破坏映射,算法就会失效。
解法四:
这应该是最优解了:每次复制原结点,将复制结点插入其后,这样形成了一个长度2倍的复合链表。对于每一个复制结点,它的random为原结点的random的next。最后再把这个复合链表拆散还原。网上有详尽图文解释,这里不再赘述。
下面是C++代码实现:
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <unordered_map> typedef int Data; struct Node { Data data; Node *next; Node *random; Node() { random = next = NULL; data = rand(); } }; Node *copy_method1(Node *list) { Node *listCopy = NULL; Node *curr = list; Node *lastCopy = NULL; while (curr) { Node *currCopy = new Node; currCopy->data = curr->data; lastCopy ? lastCopy->next = currCopy : listCopy = currCopy; lastCopy = currCopy; curr = curr->next; } for (Node *curr = list, *currCopy = listCopy; curr; curr = curr->next, currCopy = currCopy->next) { if (curr->random) { for (Node *curr1 = list, *currCopy1 = listCopy; curr1; curr1 = curr1->next, currCopy1 = currCopy1->next) if (curr1 == curr->random) currCopy->random = currCopy1; } } return listCopy; } Node *copy_method2(Node *list) { // 1. Copy regular & make mapping Node *listCopy = NULL; Node *lastCopy = NULL; std::unordered_map<Node *, Node *> raw2Copy; for (Node *curr = list; curr; curr = curr->next) { Node *currCopy = new Node; currCopy->data = curr->data; raw2Copy.insert(std::make_pair(curr, currCopy)); lastCopy ? lastCopy->next = currCopy : listCopy = currCopy; lastCopy = currCopy; } // 2. Eval random for (Node *curr = list, *currCopy = listCopy; curr; curr = curr->next, currCopy = currCopy->next) if (curr->random) currCopy->random = raw2Copy[curr->random]; return listCopy; } Node *copy_method3(Node *list) { Node *listCopy = NULL; Node *lastCopy = NULL; for (Node *curr = list; curr; curr = curr->next) { // 1. Copy regular Node *currCopy = new Node; currCopy->data = curr->data; lastCopy ? lastCopy->next = currCopy : listCopy = currCopy; // 2. Make bridge currCopy->random = curr->random; curr->random = currCopy; lastCopy = currCopy; } for (Node *curr = list, *currCopy = listCopy; curr; curr = curr->next, currCopy = currCopy->next) { // 3. Get copy's random curr->random = currCopy->random; if (currCopy->random) currCopy->random = currCopy->random->random; } return listCopy; } Node *copy_method4(Node *list) { Node *listCopy = NULL; for (Node *curr = list; curr; curr = curr->next->next) { // 1. Insert Node *currCopy = new Node; currCopy->data = curr->data; currCopy->next = curr->next; curr->next = currCopy; if (listCopy == NULL) listCopy = currCopy; } for (Node *curr = list; curr; curr = curr->next->next) { // 2. Assign copy's random if (curr->random) curr->next->random = curr->random->next; } for (Node *curr = list; curr; curr = curr->next) { // 3. Split two lists Node *actualNext = curr->next->next; if (curr->next->next) curr->next->next = curr->next->next->next; curr->next = actualNext; } return listCopy; } Node *helper_genRandList() { // Generate nodes static const int N = 10; Node *nodes[N]; srand(time(NULL)); for (int i = 0; i < N; i++) nodes[i] = new Node; // Make regular list for (int i = 0; i < N - 1; i++) nodes[i]->next = nodes[i + 1]; // Assign random random for (int i = 0; i < N - 1; i++) { // nodes[i]->random = nodes[std::max(i + 1, rand() % N)]; nodes[i]->random = nodes[rand() % N]; } return nodes[0]; } int helper_validate(Node *listA, Node *listB) { while (listA && listB) { if (listA->data != listB->data) return 1; if (listA->random || listB->random) { if (listA->random->data != listB->random->data) return 2; if (listA->random == listB->random) // Copy is incomplete return 3; } listA = listA->next; listB = listB->next; } return 0; } void helper_printList(Node *list) { printf("--------------- "); while (list) { printf("%d ", list->data); if (list->random) printf("(random)%d ", list->random->data); list = list->next; } printf("--------------- "); } int main() { Node *list = helper_genRandList(); printf("%d ", helper_validate(list, copy_method1(list))); printf("%d ", helper_validate(list, copy_method2(list))); printf("%d ", helper_validate(list, copy_method3(list))); printf("%d ", helper_validate(list, copy_method4(list))); return 0; }