Radix Sorting with using queue

落花浮王杯 提交于 2019-12-05 04:05:47

I've modified your queue a bit. To better understand the code, I use front and rear as global variables.

typedef struct queue *queue_ptr;
        struct queue {
               int data;
               queue_ptr next;
        };
queue_ptr front[10], rear[10];  /* For base 10, The 11th queue is not needed */

So the operation of adding to queue becomes

void add_queue(int i, int data) {
        queue_ptr tmp;

        tmp = (queue_ptr) malloc(sizeof(*tmp));
        tmp->next = NULL;
        tmp->data = data;
        if (front[i]) {
                rear[i]->next = tmp;
        } else {
                front[i] = tmp;
        }   
        rear[i] = tmp;
}

And add an operation of deleting from a queue(return the data as well)

int delete_queue(int i) {
        int data;
        queue_ptr tmp;

        tmp = front[i];
        if (!tmp) {
                return -1;  /* So that we can check if queue is empty */
        }   
        data = tmp->data;
        front[i] = tmp->next;
        free(tmp);
        return data;
}

So now we can implement the radix sort. It may be easier to move your data into queue with the actual numbers rather than a single digit. Note that the 11th queue is not needed if you can modify your test array *arr, and your radix_sort function could be like this:

void radix_sort(int *arr, const int size) {
        int i, j, k, radix, digits, tmp;

        if (size < 1) {
                return;  /* don't do anything if invalid size */
        }

        /* get the number of digits */
        for (digits = 0, radix = 1; arr[0] >= radix; digits++, radix *= 10);

        /* perform sort (digits) times from LSB to MSB */
        for (i = 0, radix = 1; i < digits; i++, radix *= 10) {
                /* distribute into queues */
                for (j = 0; j < size; j++) {
                        add_queue((arr[j] / radix) % 10, arr[j]);
                }
                /* take them out from each queue to the original test array */
                for (j = 0, k = 0; j < 10; j++) {
                        for (tmp = delete_queue(j); tmp != -1;\
                             tmp = delete_queue(j), k++) {
                                arr[k] = tmp;
                        }
                }
        }
}

And finally you can test by calling radix_sort(your_array, your_array_size), the full code is

#include <stdio.h>
#include <stdlib.h>

typedef struct queue *queue_ptr;
        struct queue {
               int data;
               queue_ptr next;
        };

queue_ptr front[10], rear[10];  /* For base 10, The 11th queue is not needed */

void add_queue(int i, int data) {
        queue_ptr tmp;

        tmp = (queue_ptr) malloc(sizeof(*tmp));
        tmp->next = NULL;
        tmp->data = data;
        if (front[i]) {
                rear[i]->next = tmp;
        } else {
                front[i] = tmp;
        }
        rear[i] = tmp;
}

int delete_queue(int i) {
        int data;
        queue_ptr tmp;

        tmp = front[i];
        if (!tmp) {
                return -1;  /* So that we can check if queue is empty */
        }
        data = tmp->data;
        front[i] = tmp->next;
        free(tmp);
        return data;
}

void radix_sort(int *arr, const int size) {
        int i, j, k, radix, digits, tmp;

        if (size < 1) {
                return;  /* don't do anything if invalid size */
        }

        /* get the number of digits */
        for (digits = 0, radix = 1; arr[0] >= radix; digits++, radix *=10);

        /* perform sort (digits) times from LSB to MSB */
        for (i = 0, radix = 1; i < digits; i++, radix *= 10) {
                /* distribute to queues */
                for (j = 0; j < size; j++) {
                        add_queue((arr[j] / radix) % 10, arr[j]);
                }
                /* take them out from each queue to the original test array */
                for (j = 0, k = 0; j < 10; j++) {
                        for (tmp = delete_queue(j); tmp != -1;\
                             tmp = delete_queue(j), k++) {
                                arr[k] = tmp;
                        }
                }
        }
}

int main(void) {
        int i;
        int a[5] = {133, 34, 555, 111, 12},
            b[12] = {1, 1, 1, 4, 5, 6, 7, 8, 9, 11, 11, 12};

        radix_sort(a, 5);
        radix_sort(b, 5);

        for (i = 0; i < 5; i++) {
                printf("%d ", a[i]);
        }
        printf("\n");

        for (i = 0; i < 12; i++) {
                printf("%d ", b[i]);
        }
        printf("\n");

        return 0;
}

and the output is

12 34 111 133 555
1 1 1 4 5 6 7 8 9 11 11 12

Some good information here already. At a higher level it will be difficult to debug your code because it's more complex than it needs to be. Below is a different code that uses C to express the algorithm in a more idiomatic style.

The overall point is that when it comes to code, less is usually more: Simplicity is your friend. Features shown here:

  1. Circular singly linked lists for queues. A queue is a pointer to the tail node of the list. With this, append and concatenate are fast constant-time operations.
  2. Logical, reusable functional decomposition.
  3. Only about 80 SLOC including a simple test. The sort function is 18 SLOC.
  4. Lightly tested.

Here's the sort:

// Radix sort the given queue.
void sort(QUEUE *queue)
{
  unsigned i, j, div;
  QUEUE queues[RADIX], accum;

  // Handle case of nothing to sort.
  if (*queue == NULL) return;

  // Accumulator queue initially holds unsorted input.
  accum = *queue;

  // Make one pass per radix digit.
  for (i = 0, div = RADIX; i < MAX_DIGITS; i++, div *= RADIX) {

    // Clear the radix queues.
    for (j = 0; j < RADIX; j++) queues[j] = NULL;

    // Append accumulator nodes onto radix queues based on current digit.
    NODE *p = accum, *p_next = p->next;
    do {
      // Save p->next here because append below will change it.
      p = p_next; p_next = p->next;
      append_node(&queues[p->val / div % RADIX], p);
    } while (p != accum);

    // Gather all the radix queues into the accumulator again.
    for (accum = NULL, j = 0; j < RADIX; j++) cat(&accum, queues[j]);
  }
  // Accumulator now holds sorted input.
  *queue = accum;
}

And the rest:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define RADIX 10
#define MAX_DIGITS 9

// Node and circular queue. A queue is either null or a pointer to the _tail_ node.
typedef struct node_s {
  struct node_s *next;
  unsigned val;
} NODE, *QUEUE;

// Make a new node with given value.
NODE *new_node(unsigned val)
{
  NODE *node = calloc(1, sizeof *node);
  // Must trap null return value here in production code!
  node->val = val;
  return node;
}

// Append given node to the tail of a queue.
void append_node(QUEUE *queue, NODE *node)
{
  NODE *tail = *queue;
  if (tail) {
    node->next = tail->next;
    tail->next = node;
  }
  else {
    node->next = node;
  }
  *queue = node;
}

// Concatenate the second queue onto the tail of the first. 
// First queue is changed (because it's a pointer to a tail node).
void cat(QUEUE *a, QUEUE b_tail)
{
  NODE *a_tail, *a_head;

  if (b_tail == NULL) return;
  a_tail = *a;
  if (a_tail) {
    a_head = a_tail->next;
    a_tail->next = b_tail->next;
    b_tail->next = a_head;
  }
  *a = b_tail;
}
// Sort code above goes here if you're compiling it.
// And test main goes here.

A small test main:

int main(void)
{
  int i;
  unsigned data[] = { 98, 111, 42, 1111, 21 , 997, 0, 99999, 20903};

  // Make a queue from data.
  QUEUE a = NULL;
  for (i = 0; i < sizeof data / sizeof data[0]; i++)
    append_node(&a, new_node(data[i]));

  sort(&a);

  // Print the circular list.
  NODE *p = a;
  do {
    p = p->next;
    printf("%u\n", p->val);
  } while (p != a);

  return 0;
}
James Khoury

Disclaimer: I haven't implemented a radix sort before or tested the code below. I'll leave that to you as an exercise.

When you find yourself copy-pasting something more than once stop and think: there must be a pattern.

Your switch statement is a lot of copy-pasting. In case 1: you have a line:

queue[1]->frontp = queue[0]->rearp;

I'm guessing it should be:

queue[1]->frontp = queue[1]->rearp;

If you had re-factored this code you may have been able to see this easier?

What about replacing the whole switch statement with:

int leastSignificantDigit = curNum%10;

if(queue[leastSignificantDigit]->size == 0){
    queue[leastSignificantDigit]->rearp = (queue_node_t *)malloc (sizeof(queue_node_t));
    queue[leastSignificantDigit]->frontp = queue[leastSignificantDigit]->rearp;
} else {
    queue[leastSignificantDigit]->rearp->restp = (queue_node_t *)malloc(sizeof(queue_node_t));
    queue[leastSignificantDigit]->rearp = queue[leastSignificantDigit]->rearp->restp;
}
++(queue[leastSignificantDigit]->size);
queue[leastSignificantDigit]->rearp->element = curNodep->element;
queue[leastSignificantDigit]->rearp->restp = NULL;
radixSort(curNodep->restp, queue, size);

The problem that I have seen in the first code sample is that

curNum = curNodep->element.key

curNum have always the full number and the switch statement always do curNum % 10, and this only test de last digit.

In your recursion solution (recursion is not a problem) you have to pass a parameter to know which is the digit that it have to deal.

I know this technique as immersion.

If you see the samples that you put at the end of the answer you can see that the last digit is orderer, you can change the input samples to see better this. Add big numbers with a small last number, example '901', an see the result.

Sorry for my english...

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!