2010-08-11 60 views
3

可能重复:
Copy a linked list链表问题

计算器您好!我想了解更多关于链接列表,所以我想创建一个深层复制链接列表的函数。我已经掌握了这个。难点在于输入列表将包含引用列表中其他随机节点的节点。

第一个问题是我不知道如何在列表中创建“随机”节点。截至目前,我只有'随机'节点等于'下一个'节点。

例如... pNext引用下一个值,但pReference将引用列表中的随机节点。像pReference 1参考文献3,2参考文献4,3参考文献1和4参考文献1.

第二个问题是我的代码应对'随机'节点值,但它依赖于原始副本。我希望它是一个深层复制,而不依赖于原始文件。

#include <iostream> 
#include <stdio.h> 

using namespace std; 

struct Node 
{ 
    int Number; // An integer value. 
    Node *pNext; // A pointer to the next node within the list. 
    Node *pReference; // A pointer to a random node within the list. 
}; 

void push(Node** head, int data, Node* reference) 
{ 
    Node* newNode = new Node; 
    newNode->Number = data; 
    newNode->pNext = *head; 
    newNode->pReference = reference; 
    *head = newNode; 
} 

Node* DuplicateLinkedList(Node *head) 
{ 
    Node* current = head; 
    Node* newHead = NULL; 
    Node* newTail = NULL; 

    while(current != NULL) 
    { 
     if(newHead == NULL) 
     { 
      push(&newHead, current->Number, (current->pReference)); 
      newTail = newHead; 
     } 
     else 
     { 
      push(&(newTail->pNext),current->Number, (current->pReference)); 
      newTail = newTail->pNext; 
     } 
     current = current->pNext; 
    } 

    return newHead; 
} 

int main() 
{ 
    Node* headOfList= NULL; 

    //Creating List for verification. 
    for(int i=6; i>=1;i--) 
    { 
     push(&headOfList, i, headOfList); 
    } 

    //Call duplicate function. 
    Node* copiedList = DuplicateLinkedList(headOfList); 

    //Output for verification 
    cout << endl << "Original: " << endl; 
    while(headOfList != NULL) 
    { 
     cout << "Number: " << headOfList->Number << " "; 
     cout << "pNext: " << headOfList->pNext << " "; 
     cout << "pReference: " << headOfList->pReference << " " << endl; 
     headOfList = headOfList->pNext; 
    } 
    cout << endl << endl; 

    cout << endl << "Copied: " << endl; 
    while(copiedList != NULL) 
    { 
     cout << "Number: " << copiedList->Number << " "; 
     cout << "pNext: " << copiedList->pNext << " "; 
     cout << "pReference: " << copiedList->pReference << " " << endl; 
     copiedList = copiedList->pNext; 
    } 
    cout << endl << endl; 


    system("pause"); 
} 
+0

你是什么意思的“随机节点”? – 2010-08-11 21:47:54

+0

@David Thornley:这是一个比较常见的面试问题。随机节点是同一列表中的任何一个列表元素(节点)。 – 2010-08-11 21:56:46

+0

@Moron:是的。投票关闭。但是,10分钟的工作让人分心。 – 2010-08-11 22:20:17

回答

3

使用std :: map存储原始指针和新指针之间的转换。

两次移动列表:一次创建新节点(pReference设置为NULL)并填充地图,第二次通过在地图中查找它们来填充pReference成员。

未经测试的代码:

Node* CopyNode(Node* src) 
{ 
    if (src == NULL) return NULL; 

    Node* newNode = new Node; 
    newNode->number = src->number; 
    newNode->pNext = NULL; 
    newNode->pReference = NULL; 

    return newNode; 
} 

Node* DeepCopy(Node* head) 
{ 
    if (head == NULL) return NULL; 

    std::map<Node*, Node*> mappings; 

    Node* newHead = copyNode(head); 
    mappings[head] = newHead; 

    Node* newCurrent = newHead; 
    for (Node* next = head->pNext; next != NULL; next = next->pNext) 
    { 
    Node* copy = CopyNode(next); 
    mappings[next] = copy; 

    newCurrent->pNext = copy; 
    newCurrent = copy; 
    } 

    for (Node* current = head; current != NULL; current = current->pNext) 
    { 
    Node* newCurrent = mappings[current]; 
    newCurrent->pReference = mappings[current->pReference]; 
    } 

    return newHead; 
} 
0

这是一个非常漂亮的算法,我学会了当你没有列表的大小从列表中获得一个随机元素O(N)时间。

Node* GetRandomNode(Node* head) 
{ 
int i = 1; 
srand (time (NULL)); 
Node* temp = head; 
while (head != NULL) 
{ 
    if (rand() % i == 0) temp = head; 
    head = head->pNext; 
    i++; 
} 

return temp; 
} 

所以,你可以叫你的列表的头这一功能对于每一个节点,以获得均匀分布的随机节点。

至于您的深拷贝问题,您只需分配一个新节点并将节点的值复制到其中。

+0

如果rand()永远不等于i,则永远不会分配Temp,从而导致返回未初始化的变量。 – 2010-08-11 22:02:33

+0

第一次执行循环体时,我== 0,所以总是采用if()。但是你说得对,当head为NULL时temp是未分配的。 – Sjoerd 2010-08-11 22:11:31

+0

谢谢你指出,我试图设置条件,以便第一个rand()%1将始终等于0,但是如果head为NULL,它将返回一个未初始化的变量,因此不值得付出努力。这一点实际上是1/N切换概率给你的统一分布,当有人向我展示时,我认为它是整齐的。 – 2010-08-11 22:12:37

0

如何简化。
我平时写的递归解决方案第一,然后翻译成一个循环:

Node* DuplicateLinkedList(Node* list) 
{ 
    std::map<Node*,Node*> nodeMap; 
    Node* result = DuplicateNode(nodeMap, list); 
    resetRandomNodes(nodeMap, result); 
    return result; 
} 

Node* DuplicateNode(std::map<Node*,Node*>& nodeMap, Node *node) 
{ 
    if (node== NULL) 
    { return NULL; 
    } 

    Node* result = new Node(node->Number, 
          // Recursively copy the next element 
          DuplicateNode(nodeMap, node->pNext), 
          // For now store the original rand element 
          // We will fix this by iterating over the list again 
          node->pReference 
          ); 
    // Keep a record of which node maps to which new node. 
    nodeMap[node] = result; 

    return result; 
} 

void resetRandomNodes(std::map<Node*,Node*>& nodeMap, Node *node) 
{ 
    if (node== NULL) 
    { return; 
    } 

    // Remember we stored the original random node (from the src list) in pReference. 
    // Now we must replace this with the correct value. We can look this up in the 
    // nodeMap we created when we copied the loop. 
    node->pReference = nodeMap[node->pReference]; 

    // Recursively go through list. 
    resetRandomNodes(nodeMap, node->pNext); 
} 

现在,使其有效的,你只需要递归转化为一个循环。应该是相对微不足道的。一旦你完成了,你可以将三个函数合并成一个。

+0

如果您相信您的堆栈足够大,您甚至可以编写'新节点(node-> Number,DuplicateNode(nodeMap,node-> pNext),DuplicateNode(nodeMap,node-> pReference)')如果在DuplicateNode顶部检查节点是否已经在nodeMap中,非常适合图形和其他非线性数据结构 – Sjoerd 2010-08-11 22:09:32

+0

@Sjoerd:现在你不能这样做了,对于原始节点,你将创建两个副本并递归地复制它们请记住,pReference指向同一列表中的另一个节点 – 2010-08-11 22:11:35

+0

这就是为什么我会在DuplicateNode的顶部添加一个附加项来检查节点是否已经在nodeMap中:'if(nodeMap [node]!= NULL )return nodeMap [node];' – Sjoerd 2010-08-11 22:14:51