I'm trying to implement radix sort on a linked list based on an integer with the code below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_DIGITS 10 // maximum number of digits in a key
// Structure for a node in the linked list
typedef struct node
{
int key; // the key to be sorted
char value[20]; // the value associated with the key
struct node *next; // pointer to the next node in the list
} Node;
// Function prototypes
void radixSort(Node **head);
Node *createNode(int key, const char *value);
Node *append(Node *head, int key, const char *value);
void printList(Node *head);
int main(void)
{
Node *head = NULL; // head of the linked list
// Create a linked list with some random keys and values
head = append(head, 456, "apple");
head = append(head, 345, "banana");
head = append(head, 123, "cherry");
head = append(head, 789, "date");
head = append(head, 231, "elderberry");
head = append(head, 567, "fig");
head = append(head, 876, "grape");
// Print the original list
printf("Original list:\n");
printList(head);
// Sort the list using radix sort
radixSort(&head);
// Print the sorted list
printf("Sorted list:\n");
printList(head);
return 0;
}
// Function to sort the linked list using radix sort
void radixSort(Node **head)
{
Node *bucket[10]; // array of buckets
Node *curr; // pointer to the current node
Node *tail[10]; // array of tails for each bucket
int i, j, k;
int factor; // factor to sort on
int digits[MAX_DIGITS]; // array to store the digits of the keys
// Initialize the buckets and tails
for (i = 0; i < 10; i++)
{
bucket[i] = NULL;
tail[i] = NULL;
}
// Find the maximum number of digits in the keys
int maxDigits = 0;
curr = *head;
while (curr != NULL)
{
int key = curr->key;
int numDigits = 0;
while (key > 0)
{
numDigits++;
key /= 10;
}
if (numDigits > maxDigits)
{
maxDigits = numDigits;
}
curr = curr->next;
}
// Loop through each digit, starting with the least significant digit
for (factor = 1; maxDigits > 0; factor *= 10, maxDigits--)
{
// Extract the digits of the keys
curr = *head;
while (curr != NULL)
{
digits[curr->key / factor % 10]++;
curr = curr->next;
}
// Cumulative sum of the digits
for (i = 1; i < 10; i++)
{
digits[i] += digits[i - 1];
}
// Sort the nodes into the appropriate buckets
curr = *head;
while (curr != NULL)
{
int digit = curr->key / factor % 10;
if (bucket[digit] == NULL)
{
bucket[digit] = curr;
tail[digit] = curr;
}
else
{
tail[digit]->next = curr;
tail[digit] = curr;
}
curr = curr->next;
}
// Rebuild the list in sorted order
*head = NULL;
for (i = 9; i >= 0; i--)
{
//printf("%dA\n",i);
if (bucket[i] != NULL)
{
//printf("%dB\n",i);
if (*head == NULL)
{
*head = bucket[i];
// printf("%dC\n",i);
}
else
{
//printf("%dD\n",i);
tail[9 - i]->next = bucket[i];
//printf("%dF\n",i);
}
// tail[9 - i] = tail[i];
}
else{
// printf("%dE\n",i);
}
//printf("here\n");
}
}
}
// Function to create a new node with the given key and value
Node *createNode(int key, const char *value)
{
Node *node = (Node*) malloc(sizeof(Node));
node->key = key;
strcpy(node->value, value);
node->next = NULL;
return node;
}
// Function to append a new node with the given key and value to the end of the list
Node *append(Node *head, int key, const char *value)
{
Node *newNode = createNode(key, value);
Node *curr = head;
if (head == NULL)
{
return newNode;
}
while (curr->next != NULL)
{
curr = curr->next;
}
curr->next = newNode;
return head;
}
// Function to print the linked list
void printList(Node *head)
{
Node *curr = head;
while (curr != NULL)
{
printf("(%d, %s) ", curr->key, curr->value);
curr = curr->next;
}
printf("\n");
}
The segmentation fault occurs when tail[9 - i]->next = bucket[i];
is executed.
I added printf
statements (turned them into comment blocks) to trace where the error is. Can someone please help me figure out a way to get this to work?
There are a few issues:
tail[9 - i]->next = bucket[i];
sets the wrong pointer. There is no reason why it should be 9 - i
. It could even be that tail[9 - i]
is NULL. So this leads to indefined behaviour. Instead you should keep track of what the current tail node is in the list that is being built. You could use curr
for that purpose. Let it start (before the loop) with curr = NULL
and then when you find a non-null bucket, end that process by setting curr = tail[i]
. When a bucket is found when the *head
is no longer NULL
, make the link with curr->next = bucket[i]
.
The new list is not terminated with a NULL
pointer. When the above point is implemented, continue after the loop with curr->next = NULL
.
In the next iteration of the factor
loop, both digits
and buckets
need to be reset. You could do this with a loop or with memset
.
Here is the corrected factor
loop code -- comments indicate where corrections were made:
for (factor = 1; maxDigits > 0; factor *= 10, maxDigits--)
{
memset(digits, 0, sizeof(digits)); // reset!
memset(bucket, 0, sizeof(bucket)); // reset!
curr = *head;
while (curr != NULL)
{
digits[curr->key / factor % 10]++;
curr = curr->next;
}
for (i = 1; i < 10; i++)
{
digits[i] += digits[i - 1];
}
curr = *head;
while (curr != NULL)
{
int digit = curr->key / factor % 10;
if (bucket[digit] == NULL)
{
bucket[digit] = curr;
tail[digit] = curr;
}
else
{
tail[digit]->next = curr;
tail[digit] = curr;
}
curr = curr->next;
}
*head = NULL;
curr = NULL; // <-- track the current tail of the newly built list
for (i = 9; i >= 0; i--)
{
if (bucket[i] != NULL)
{
if (*head == NULL)
{
*head = bucket[i];
}
else
{
curr->next = bucket[i]; // Append bucket after the current tail
}
curr = tail[i]; // The tail is now at the end of this bucket
}
}
curr->next = NULL; // Terminate the list properly
}