views:

116

answers:

3

Possible Duplicate:
Copy a linked list

Hello stackoverflow! I am trying to learn more about linked lists so I am trying to create a function that deep copies a linked list. I've got this under control. The hard part is that the input list is going to contain nodes that reference other random nodes within the list.

First problem is that I don't know how to create the 'random' nodes within the list. As of now, I just have the 'random' nodes equal to the 'next' nodes.

For example... pNext references the next value, but pReference will reference a random node in the list. Like pReference 1 references 3, 2 references 4, 3 references 1, and 4 references 1.

Second problem is that my code is coping the 'random' node values but it is dependent on the original copy. I want it to be a deep copy and not dependent on the original.

#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");
}
+3  A: 

Use a std::map to store the conversion between the original and the new pointers.

Walk the list two times: one time to create the new nodes (with pReference set to NULL) and to populate the map, a second time to fill in the pReference member by looking them up in the map.

Untested code:

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;
}
Sjoerd
A: 

Here is a very slick algorithm I learned to get a random element from a list in O(N) time when you don't have the size of the list.

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;
}

So you can just call this function with the head of your list for each node to get a uniformly distributed random node.

As for your deep copy problem, you just have to allocate a new node and copy the value of your node into it.

FranticPedantic
Temp is never assigned if rand() never equals i, leading to you returning an uninitialized variable.
Clark Gaebel
The first time the loop body is executed, i == 0 so the if() is always taken. But you're right that temp is unassigned when head is NULL.
Sjoerd
Thanks for pointing that out, I was trying to set up the conditional so that the first rand() % 1 will always equal 0, but if head is NULL it will return an uninitialized variable so it's not worth the effort. The point was really the 1/N probability of switching gives you the uniform distribution which I thought was neat when someone showed it to me.
FranticPedantic
-1 as this doesn't answer the question at all. The question was not "how do I select a random element".
Sjoerd
@Sjoerd "First problem is that I don't know how to create the 'random' nodes within the list." Sorry if I misunderstood that?
FranticPedantic
@FranticPedantic: ok, I agree that OP wasn't clear. I've cancelled the downvote.
Sjoerd
A: 

How about simplifying.
I usually write the recursive solution first then translate that into a loop:

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);
}

Now to make it efficient you just need to translate the recursion into a loop. Should be relatively trivial. Once you have done that you can merge the three functions into one.

Martin York
If you trust that your stack is large enough, you could even write 'new Node(node->Number, DuplicateNode(nodeMap, node->pNext), DuplicateNode(nodeMap, node->pReference)', if you add a simple if at the top of DuplicateNode to check whether node is already in the nodeMap. Great for graphs and other non-linear datastructures.
Sjoerd
@Sjoerd: Now you cant do that. For every node in the original you are then creating two copies and recursively copying them. Remember that pReference points at a another node in the same list.
Martin York
@Martin: That's why I would add an additional if at the top of DuplicateNode to check whether node is already in the nodeMap: 'if (nodeMap[node] != NULL) return nodeMap[node];'
Sjoerd
@Sjoerd: I see that. I did exactly the same.
Martin York