/*

 * merge_sort.c

 * This program sorts a sequence of number using the merge-sort algorithm.

 */

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <math.h>

 

/* Definition of a list element: */

struct _Element

{

  int              value;    /* The stored value. */

  struct _Element  *p_next;  /* Points to the next observation. */

};

 

typedef struct _Element Element;

 

/* Prototypes: */

void print_list (const Element *p_head);

void merge_sort (Element **p_p_head, int size);

 

int main ()

{

  int         n;                /* How many numbers to sort. */

  int         max;              /* The maximal value of the numbers. */

  Element     *p_head = NULL;   /* Points to the head of the list. */

  Element     *p_curr = NULL;   /* The current element. */

  int         i;

 

  /* Read the input from the user. */

  do

  {

    printf ("Please enter the number of numbers to sort: ");

    scanf ("%d", &n);

  } while (n <= 0);

 

  do

  {

    printf ("Please enter the maximal number allowed: ");

    scanf ("%d", &max);

  } while (max <= 0);

 

  /* To make things more random: */

  srand (time(NULL));

 

  /* Create a list of n random number in the range of [0 - max]. */

  for (i = 0; i < n; i++)

  {

    /* Create a new element storing a random number. */

    p_curr = (Element *) malloc (sizeof(Element));

    p_curr->value = (int) (max * (rand() / (RAND_MAX + 1.0)));

   

    /* Link it to be the first element in the list. */

    p_curr->p_next = p_head;

    p_head = p_curr;

  }

 

  /* Print the list. */

  printf ("The original list: ");

  print_list (p_head);

 

  /* Sort the list. */

  merge_sort (&p_head, n);

 

  printf ("The sorted list: ");

  print_list (p_head);

 

  /* Free the list. */

  while (p_head != NULL)

  {

    p_curr = p_head;

    p_head = p_head->p_next;

    free (p_curr);

  }

 

  return (1);

}

 

/* ------------------------------------------------------------------------

 * Function: print_list

 * Purpose:  Print a list.

 * Input:    p_head - The first element in the list.

 * Returns:  Nothing.

 */

void print_list (const Element *p_head)

{

  while (p_head != NULL)

  {

    /* Print the current element. */

    printf ("%d ", p_head->value);

   

    /* Move to the next element. */

    p_head = p_head->p_next;

  }

 

  printf ("\n");

  return;

}

 

/* ------------------------------------------------------------------------

 * Function: merge_sort

 * Purpose:  Sort the list using the merge-sort alogrithm.

 * Input:    size - The list size (number of elements in the list).

 * In/Out:   p_p_head - The first element in the list.

 * Returns:  Nothing.

 */

void merge_sort (Element **p_p_head, int size)

{

  Element       *p_head_1;         /* The first list.*/

  int           size_1;            /* Its size. */

  Element       *p_head_2;         /* The second list.*/

  int           size_2;            /* Its size. */

  Element       *p_curr;

  Element       *p_merged_head = NULL;

  Element       *p_merged_tail = NULL;

  int           i;

 

  /* Do nothing if the list has only one element. */

  if (size < 2)

    return;

 

  /* Split the list into two list of equals sizes. */

  size_1 = size / 2;

  p_head_1 = *p_p_head;

 

  p_curr = *p_p_head;

  for (i = 0; i < size_1 - 1; i++)

    p_curr = p_curr->p_next;

 

  size_2 = size - size_1;

  p_head_2 = p_curr->p_next;

  p_curr->p_next = NULL;

 

  /* Sort the two list recursively. */

  merge_sort (&p_head_1, size_1);

  merge_sort (&p_head_2, size_2);

 

  /* Merge the two lists into one sorted list. */

  while (p_head_1 != NULL && p_head_2 != NULL)

  {

    /* Check which list contains the minimal value in its first element,

     * and move this element to be the tail of the merged list. */

    if (p_head_1->value < p_head_2->value)

    {

      p_curr = p_head_1;

      p_head_1 = p_head_1->p_next;

    }

    else

    {

      p_curr = p_head_2;

      p_head_2 = p_head_2->p_next;

    }

   

    if (p_merged_head == NULL)

    {

      /* This is the first element in the merged list: */

      p_merged_head = p_merged_tail = p_curr;

    }

    else

    {

      /* Add the element at the tail of the merged list: */

      p_merged_tail->p_next = p_curr;

      p_merged_tail = p_curr;

    }

    p_curr->p_next = NULL;

  }

 

  /* Add any left-overs at the tail of the merged list. */

  if (p_head_1 != NULL)

  {

    p_merged_tail->p_next = p_head_1;

  }

  else if (p_head_2 != NULL)

  {

    p_merged_tail->p_next = p_head_2;

  }

 

  /* Update the pointer to the head of the merged list. */

  *p_p_head = p_merged_head;

 

  return;

}