Monday, November 1, 2010

Data Structures Part 4

Given only a pointer to a node to be deleted in a singly linked list, how do you delete it? 

          
#include 
#include 

struct linkedList{
 int element;
 struct linkedList* next;
};

typedef struct linkedList* List;

void deleteNode(List Node)
{
 List tmp;
 if(Node){ // If current node is not NULL
  tmp = List->next; // take backup of next Node 
 Node->element = Node->next->element; // replace current node element 
with next Node element 
 Node->next = Node->next->next; // change next pointer to next to 
                                            next 
  free(tmp); // free the next Node which was taken backup
 }
 return;
}


How do you reverse a linked list without using any C pointers? 

One way is to reverse the data in the nodes without changing the pointers 
themselves.One can also create a new linked list which is the reverse of 
the original linked list. A simple C program can do that for you. Please
note that you would still use the "next" pointer fields to traverse through
the linked list (So in effect, you are using the pointers, but you are not
changing them when reversing the linked list).


How can I search for data in a linked list? 

The only way to search a linked list is with a linear search, because
the only way a linked list?s members can be accessed is sequentially. 
Sometimes it is quicker to take the data from a linked list and store 
it in a different data structure so
that searches can be more efficient. 

How would you find out if one of the pointers in a linked list is corrupted or not? 

          

This is a really good interview question. The reason is that linked lists
are used in a wide variety of scenarios and being able to detect and correct
pointer corruptions might be a very valuable tool. For example, data blocks
associated with files in a file system are usually stored as linked lists.
Each data block points to the next data block. A single corrupt pointer
can cause the entire file to be lost! 

Discover and fix bugs when they corrupt the linked list and not when 
effect becomes visible in some other part of the program. 
Perform frequent consistency checks (to see if the linked list
is indeed holding the data that you inserted into it). 

It is good programming practice to set the pointer value to NULL immediately
after freeing the memory pointed at by the pointer. This will help in 
debugging,because it will tell you that the object was freed somewherebeforehand. 
Keep track of how many objects are pointing to a object using 
reference counts if required. 

Use a good debugger to see how the datastructures are getting corrupted
and trace down the problem. Debuggers like ddd on linux and memory profilers
like Purify,Electric fence are good starting points. These tools should help
you track down heap corruption issues easily. 

Avoid global variables when traversing and manipulating linked lists.
Imagine what would happen if a function which is only supposed to traverse a
linked list using a global head pointer accidently sets the head pointer to 
NULL!

Its a good idea to check the addNode() and the deleteNode() routines and 
test them for all types of scenarios. This should include tests for 
inserting/deleting nodes at the front/middle/end of the linked list, working
with an empty linked list, 
run out of memory when using malloc() when allocating memory for new nodes,
writing through NULL pointers, writing more data into the node fields then  
they can hold (resulting in corrupting the (adjacent) "prev" and "next"
pointer fields), make sure bug fixes and enhancements to the linked list code 
are reviewed and well tested (a lot of bugs come from quick and dirty bug fixing),
log and handle all possible errors , add multiple levels of logging 
so that you can dig through the logs. The list is endless... 

Each node can have an extra field associated with it. This field indicates 
the number of nodes after this node in the linked list. This extra field needs
to be kept up-to-date when we inserte or delete nodes in the linked list 
(It might become slightly complicated when insertion or deletion happens not 
at end, but anywhere in the linked list).Then, if for any node, p->field >
0 and p->next == NULL, it surely points to a pointer corruption. 

You could also keep the count of the total number of nodes in a linked list and use
it to check if the list is indeed having those many nodes or not. 


The problem in detecting such pointer corruptions in C is that its only the 
programmer who knows that the pointer is corrupted. The program has no way of
knowing that something is wrong. so the best way to fix errors is check Ur 
logic and test your code to the max possible event.
If you detect a cycle in the link list, it's definitely bad. 
If it's a doubly linked list you can verify, pNode->Next->Prev== pNode. 


I have a hunch that interviewers who ask this question are probably hinting at 
something called Smart Pointers in C++. Smart pointers are particularly useful
in the face of exceptions as they ensure proper destruction of dynamically
allocated objects.They can also be used to keep track of dynamically allocated 
objects shared by multiple owners. This topic is out of scope here, but you can
find lots of material on the Internet for Smart Pointers. 

Data Structures Part 3

How do you sort a linked list? Write a C program to sort a linked list. 

         

This is a very popular interview question, which most people go wrong.
The ideal solution to this problem is to keep the linked list sorted 
as you build it. Another question on this website teaches you how to 
insert elements into a linked list in the sorted order. This really 
saves a lot of time which would have been required to sort it. 

However, you need to Get That Job....

Method1 (Usual method)

The general idea is to decide upon a sorting algorithm 
(say bubble sort). Then, one needs to come up with different scenarios
to swap two nodes in the linked list when they are not in the required 
order. The different scenarios would be something like

1. When the nodes being compared are not adjacent and one of them
   is the first node.
2. When the nodes being compared are not adjacent and none of them 
   is the first node
3. When the nodes being compared are adjacent and one of them 
   is the first node.
4. When the nodes being compared are adjacent and none of them 
   is the first node.


 example bubble sort for a linked list goes like this (working C code!)...


#include
#include

typedef struct node
{
  int value;
  struct node *next;
  struct node *prev;
}mynode ;

void add_node(struct node **head, int *count, int value);
void print_list(char *listName, struct node *head);
mynode *bubbleSort(mynode *head, int count);


int main()
{
 mynode *head;
 int count = 0;
 
 head = (struct node *)NULL;
 
 add_node(&head, &count, 100);
 add_node(&head, &count, 3);
 add_node(&head, &count, 90);
 add_node(&head, &count, 7);
 add_node(&head, &count, 9);

 print_list("myList(BEFORE)", head);
 head = bubbleSort(head, count);
 print_list("myList(AFTER) ", head);


 getch();
 return(0);

}


mynode *bubbleSort(mynode *head, int count)
{
  int i, j;
  mynode *p0, *p1, *p2, *p3;
  
  for(i = 1; i < count; i++)
  {
    p0 = (struct node *)NULL;
    p1 = head;
    p2 = head->next;
    p3 = p2->next;
  
    for(j = 1; j <= (count - i); j++)
    {
       if(p1->value > p2->value)  
       {
           // Adjust the pointers...       
           p1->next = p3;
           p2->next = p1;
           if(p0)p0->next=p2;
           

           // Set the head pointer if it was changed...
           if(head == p1)head=p2;


           // Progress the pointers
           p0 = p2;
           p2 = p1->next;
           p3 = p3->
next!=(struct node *)NULL?p3->next: (struct node *)NULL; 

       }
       else
       {
          // Nothing to swap, just progress the pointers...
          p0 = p1;
          p1 = p2;
          p2 = p3;
 p3 = p3->next!=(struct node *)NULL?p3->next: (struct node *)NULL; 
       }
    }
  }

  return(head);
}


void add_node(struct node **head, int *count, int value)
{
  mynode *temp, *cur;
  temp = (mynode *)malloc(sizeof(mynode));
  temp->next=NULL;
  temp->prev=NULL;

  if(*head == NULL)
  {
     *head=temp;
     temp->value=value;
  }
  else
  {
   for(cur=*head;cur->next!=NULL;cur=cur->next);
   cur->next=temp;
   temp->prev=cur;
   temp->value=value;
  }
  
  *count = *count + 1;
}


void print_list(char *listName, struct node *head)
{
  mynode *temp;

  printf("\n[%s] -> ", listName);
  for(temp=head;temp!=NULL;temp=temp->next)
  {
    printf("[%d]->",temp->value);
  }
  
  printf("NULL\n");

}



As you can see, the code becomes quite messy because of the pointer logic.
Thats why I have not elaborated too much on the code, nor on variations 
such as sorting a doubly linked list. You have to do it yourself once to
understand it. 



Method2 (Divide and Conquer using Merge Sort) 


Here is some cool working C code...



#include
#include

typedef struct node
{
  int value;
  struct node *next;
  struct node *prev;
}mynode ;



void add_node(struct node **head, int value);
void print_list(char *listName, struct node *head);
void mergeSort(struct node** headRef);
struct node *merge2SortedLLs(struct node *head1, struct node *head2);
void splitLLInto2(struct node* source, struct node** frontRef, 
                  struct node** backRef);



// The main function..
int main()
{
   mynode *head;
   head = (struct node *)NULL;
 
   add_node(&head, 1);
   add_node(&head, 10);
   add_node(&head, 5);
   add_node(&head, 70);
   add_node(&head, 9);
   add_node(&head, -99);
   add_node(&head, 0);

 
   print_list("myList", head);
   mergeSort(&head);
   print_list("myList", head);

   getch();
   return(0);
}




// This is a recursive mergeSort function...
void mergeSort(struct node** headRef) 
{
  struct node* head = *headRef;
  struct node* a;
  struct node* b;

  // Base case -- length 0 or 1
  if ((head == NULL) || (head->next == NULL)) 
  {
    return;
  }
  
  // Split head into 'a' and 'b' sublists
  splitLLInto2(head, &a, &b); 

  // Recursively sort the sublists
  mergeSort(&a); 
  mergeSort(&b);

  // Merge the two sorted lists together
  *headRef = merge2SortedLLs(a, b); 
}





// This is an iterative function that joins two already sorted 
// Linked lists...
struct node *merge2SortedLLs(struct node *head1, struct node *head2)
{
   struct node *a, *b, *c, *newHead, *temp;
   
   a = head1;
   b = head2;
   c       = (struct node *)NULL;
   newHead = (struct node*)NULL;
   
   
   if(a==NULL)return(b);
   else if(b==NULL)return(a);
   
   while(a!=NULL && b!=NULL)
   {
     if(a->value < b->value)
     {
       //printf("\na->value < b->value\n");
       if(c==NULL)
       {
         c  = a;
       }
       else
       {
         c->next = a;
         c = c->next;
       }
       a  = a->next;    
     }
     else if(a->value > b->value)
     { 
       //printf("\na->value > b->value\n");
       if(c==NULL)
       {
         c  = b;
       }
       else
       {
         c->next = b;
         c = c->next;
       }
       b  = b->next;
     }
     else
     {
       // Both are equal.
       // Arbitraritly chose to add one of them and make
       // sure you skip both!

       if(c == NULL)
       {
         c  = a;
       }
       else
       {
         c->next = a;
         c = c->next;
       }
       
       a  = a->next;
       b  = b->next;
     }
     
     // Make sure the new head is set...
     if(newHead == NULL) 
      newHead = c;
   
   }
   
   if(a==NULL && b==NULL)
     return(newHead);
     
   if(a==NULL)
     c->next = b;
   else if(b==NULL)
     c->next = a;
     
   return(newHead);
   
}


// Uses the fast/slow pointer strategy
//
// This efficient code splits a linked list into two using
// the same technique as the one used to find the 
// middle of a linked list!

void splitLLInto2(struct node* source, struct node** frontRef, 
                  struct node** backRef)

{
  struct node* fast;
  struct node* slow;
  
  if (source==NULL || source->next==NULL) 
  { 
    // length < 2 cases
    *frontRef = source;
    *backRef = NULL;
  }
  else 
  {
    slow = source;
    fast = source->next;
    // Advance 'fast' two nodes, and advance 'slow' one node
    while (fast != NULL) 
    {
       fast = fast->next;
       if (fast != NULL) 
       {
          slow = slow->next;
          fast = fast->next;
       }
    }
    // 'slow' is before the midpoint in the list, so split it in two
    // at that point.
    *frontRef = source;
    *backRef = slow->next;
    slow->next = NULL;
  }
}


void add_node(struct node **head, int value)
{
  mynode *temp, *cur;
  temp = (mynode *)malloc(sizeof(mynode));
  temp->next=NULL;
  temp->prev=NULL;

  if(*head == NULL)
  {
     *head=temp;
     temp->value=value;
  }
  else
  {
   for(cur=*head;cur->next!=NULL;cur=cur->next);
   cur->next=temp;
   temp->prev=cur;
   temp->value=value;
  }
}


void print_list(char *listName, struct node *head)
{
  mynode *temp;

  printf("\n[%s] -> ", listName);
  for(temp=head;temp!=NULL;temp=temp->next)
  {
    printf("[%d]->",temp->value);
  }
  
  printf("NULL\n");

}



The code to merge two already sorted sub-linked lists into a 
sorted linked list could be either iterative or recursive. 
You already saw the iterative version above. Here  is a 
recursive version of the same... 


Recursive solution to merge two already sorted linked lists
into a single linked list 


struct node* sortedMergeRecursive(struct node* a, struct node* b) 
{
  struct node* result = NULL;

  if (a==NULL) return(b);
  else if (b==NULL) return(a);

  // Pick either a or b, and recur
  if (a->data <= b->data) 
  {
     result = a;
     result->next = sortedMergeRecursive(a->next, b);
  }
  else 
  {
     result = b;
     result->next = sortedMergeRecursive(a, b->next);
  }
  
  return(result);
}




Also, see how the splitLLInto2() function uses the same technique used
to find the middle of a linked list to split a linked list into two
without having to keep a count of the number of nodes in the linked list.st! 

Here is another solution (not that great, though) to split a linked list 
into two.It used the count of the number of nodes to decide where to split 


void splitLLInto2(struct node* source,struct node** frontRef,
                    struct node** backRef)

{
  int len = Length(source); //Get the length of the original LL..
  int i;
  struct node* current = source;
  
  if (len < 2) 
  {
    *frontRef = source;
    *backRef = NULL;
  }
  else 
  {
     int hopCount = (len-1)/2; 

     for (i = 0; inext;
     }
     
     // Now cut at current
    *frontRef = source;
    *backRef = current->next;
    current->next = NULL;
  }
}



Using recursive stack space proportional to the length of a list 
is not recommended.However, the recursion in this case is ok ? it 
uses stack space which is proportional to the log of the length of the list. 
For a 1000 node list, the recursion will only go about 10 deep. 
For a 2000 node list, it will go 11 deep. If you think about it,
you can see that doubling the size of the list only increases the depth by 1. 

Data Structures Part 2

How do you reverse a singly linked list? How do you reverse a doubly linked list? Write a C program to do the same. 

          
             

                     /* SINGLY LINKED LIST */

#include 
#include 

struct linkedList{
 int element;
 struct linkedList *next;
};

typedef struct linkedList* List;

List reverseList(List L)
{
 List tmp, previous=NULL;

 while(L){
  tmp = L->next;
  L->next = previous;
  previous = L;
  L = tmp;
 }
 L = previous;
 return L;
 
}

List recursiveReverse(List L)
{
 List first, rest;
 if(!L)
  return NULL;
 first = L; 
 rest = L->next;
 if(!rest)
  return NULL;
 rest = recursiveReverse(rest);
 first->next->next = first;
 first->next = NULL;
 L=rest;

 return L;
}