Skip to lab content

WEEK 4 · ADVANCED DATA STRUCTURES LAB

Radix sort — stable decimal passes

B.Tech CSE & Allied Branches · II Year, I Semester · AR25C173 tested sample runs

THE SYLLABUS TASK

What you need to solve

Implement non-comparison-based Radix Sort for integers in ascending order.
Input format & conventions

n (1–100), then n nonnegative integers from 0 through 1,000,000. Negative values are intentionally rejected by this LSD decimal version.

UNDERSTAND THE IDEA

Explanation

LSD radix sort orders values by the ones digit, then tens, then hundreds and so on. It uses digit values rather than direct key comparisons.

Each pass performs stable counting sort with ten counters. Stability is essential because later digits must not undo the order established by earlier digits.

The maximum value determines how many decimal places are needed. A wide place variable and the bounded accepted values keep multiplication by ten safe.

TURN THE IDEA INTO STEPS

Step-by-step algorithm

  1. Find the maximum accepted value.
  2. For each needed decimal place, count occurrences of digits 0…9.
  3. Convert counts to cumulative end positions.
  4. Place input values into output from right to left to preserve equal-digit order.
  5. Copy output back after each pass and print the final array.

SEE THE CONTROL FLOW

Flowcharts

Diamonds show decisions; labeled arrows select a branch. Returning arrows repeat work. Helper-function steps group the operations explained above and in the code. These charts focus on the named core operation. On a phone, scroll within the diagram or open the full-size version.

Stable decimal digit passes

Stable decimal digit passes

Open full-size flowchart ↗

C17

Complete C program

Download .c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

/* Read one entire token; reject malformed, oversized and out-of-range input. */
int readToken(char text[], size_t capacity) {
    int ch; size_t used = 0; int fits = 1;
    while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
    if (ch == EOF) return 0;
    do {
        if (ch == 0) fits = 0;
        if (used + 1 < capacity) text[used++] = (char)ch;
        else fits = 0;
        ch = getchar();
    } while (ch != EOF && !isspace((unsigned char)ch));
    text[used] = '\0';
    return fits;
}
int readInt(int *value) {
    char token[64], *end;
    if (!readToken(token, sizeof token)) return 0;
    errno = 0; long result = strtol(token, &end, 10);
    if (errno || end == token || *end || result < -1000000 || result > 1000000) return 0;
    *value = (int)result; return 1;
}
int invalid(void) { puts("Invalid or incomplete input."); return 1; }

#define MAX_N 100
int readArray(int a[], int *n, int nonnegative) {
    if (!readInt(n) || *n < 1 || *n > MAX_N) return 0;
    for (int i = 0; i < *n; ++i)
        if (!readInt(&a[i]) || (nonnegative && a[i] < 0)) return 0;
    return 1;
}
void printArray(const int a[], int n) {
    printf("Sorted:"); for (int i = 0; i < n; ++i) printf(" %d",a[i]); putchar('\n');
}

void digitPass(int a[], int n, long long place) {
    int count[10] = {0}, output[MAX_N];
    for (int i = 0; i < n; ++i) ++count[(a[i] / place) % 10];
    for (int d = 1; d < 10; ++d) count[d] += count[d - 1];
    for (int i = n - 1; i >= 0; --i) {
        int digit = (int)((a[i] / place) % 10);
        output[--count[digit]] = a[i];
    }
    for (int i = 0; i < n; ++i) a[i] = output[i];
}
void radixSort(int a[], int n) {
    int max = a[0]; for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i];
    /* Input max is 1,000,000; long long place avoids int exponent overflow. */
    for (long long place = 1; max / place; place *= 10) digitPass(a,n,place);
}
int main(void) {
    int a[MAX_N], n; if (!readArray(a,&n,1)) return invalid();
    radixSort(a,n); printArray(a,n); return 0;
}

Local compile: gcc -std=c17 radix-sort.c -o program -lm

Open in C compiler transfers the source. Copy the sample input into the compiler’s input box separately before running.

UNDERSTAND THE CODE

Line-by-line explanation

Follow the exact source above, from input handling through the algorithm and its result. Line numbers count blank lines; related statements are grouped into steps. Start with main to follow execution, then open the functions it calls.

Start with main → · Algorithm and data structure →

Lines 1–5Include input, conversion and validation declarations
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

stdio.h supplies terminal input/output. stdlib.h declares conversion and allocation functions. string.h supplies string and memory utilities. ctype.h supplies isspace, and errno.h supplies conversion error reporting. Headers declare facilities; including them does not execute an operation.

Lines 7–9Define a complete-token reader and initialise its state
/* Read one entire token; reject malformed, oversized and out-of-range input. */
int readToken(char text[], size_t capacity) {
    int ch; size_t used = 0; int fits = 1;

readToken receives a writable buffer and its capacity. ch is int so it can represent EOF as well as input characters. used counts stored characters, and fits starts as a success flag. One slot must remain for the null terminator.

Lines 10–11Skip leading whitespace and recognise end-of-file
    while ((ch = getchar()) != EOF && isspace((unsigned char)ch)) { }
    if (ch == EOF) return 0;

Read until a non-whitespace character appears. The unsigned-char cast supplies an allowed value to isspace. Return 0 if EOF arrives before a token begins.

Lines 12–15Collect a token without silently truncating it
    do {
        if (ch == 0) fits = 0;
        if (used + 1 < capacity) text[used++] = (char)ch;
        else fits = 0;

The do body handles the character already read. An embedded zero byte marks the token invalid. Store a character only while there is room for it and a null terminator; otherwise clear fits. Continue reading even after the buffer fills.

Lines 16–20Consume the full token and terminate the buffer
        ch = getchar();
    } while (ch != EOF && !isspace((unsigned char)ch));
    text[used] = '\0';
    return fits;
}

Read through the next whitespace or EOF, add a null character after the stored prefix, then return fits. A too-long token returns 0 rather than pretending that its stored prefix is the complete token.

Lines 21–23Wrap token input as a checked integer read
int readInt(int *value) {
    char token[64], *end;
    if (!readToken(token, sizeof token)) return 0;

value is an output address. A 64-slot local buffer receives the token; a failed token read returns 0 before attempting conversion.

Lines 24–25Convert base-ten text and check the whole result
    errno = 0; long result = strtol(token, &end, 10);
    if (errno || end == token || *end || result < -1000000 || result > 1000000) return 0;

Reset errno before strtol. &end receives the stopping address. Reject conversion errors, no converted digits, leftover characters, or values outside -1,000,000 to 1,000,000. Thus 12x is not accepted as the integer 12.

Lines 26–28Return the accepted value or a standard error status
    *value = (int)result; return 1;
}
int invalid(void) { puts("Invalid or incomplete input."); return 1; }

Store a validated integer through *value and return 1 from readInt. invalid is a separate helper that prints the error and returns 1 as main’s failure status; the same number has different roles in these two function contracts.

Lines 30–32Read a bounded nonempty array length
#define MAX_N 100
int readArray(int a[], int *n, int nonnegative) {
    if (!readInt(n) || *n < 1 || *n > MAX_N) return 0;

MAX_N is 100. readArray receives an array, the caller’s count address, and a flag requesting nonnegative values. Require a count from 1 to 100; return 0 before using an invalid count.

Lines 33–36Read every element and apply the requested sign rule
    for (int i = 0; i < *n; ++i)
        if (!readInt(&a[i]) || (nonnegative && a[i] < 0)) return 0;
    return 1;
}

readInt checks each complete integer token. If nonnegative is nonzero, negative elements are also rejected. Return 1 only after every requested element has been accepted.

Lines 37–39Display the used array elements
void printArray(const int a[], int n) {
    printf("Sorted:"); for (int i = 0; i < n; ++i) printf(" %d",a[i]); putchar('\n');
}

printArray reads the first n elements without modifying them. It prints the Sorted label, a space before each value, and a final newline.

Lines 41–42Prepare a stable counting pass for one decimal position
void digitPass(int a[], int n, long long place) {
    int count[10] = {0}, output[MAX_N];

place identifies units, tens, hundreds and so on. count has ten zeroed digit counters, and output stores this pass’s rearranged values.

Lines 43–44Count digits and convert frequencies to end positions
    for (int i = 0; i < n; ++i) ++count[(a[i] / place) % 10];
    for (int d = 1; d < 10; ++d) count[d] += count[d - 1];

(a[i] / place) % 10 selects the current decimal digit. Prefix sums make count[d] equal the number of values whose selected digit is at most d.

Lines 45–48Place equal-digit values in stable order
    for (int i = n - 1; i >= 0; --i) {
        int digit = (int)((a[i] / place) % 10);
        output[--count[digit]] = a[i];
    }

Scan the input backward. Decrement count[digit] to obtain that item’s output index, then store it. Backward scanning preserves the input order of equal-digit items, which later digit passes depend on.

Lines 49–50Copy this pass back into the array
    for (int i = 0; i < n; ++i) a[i] = output[i];
}

Replace the used input elements with output. The next decimal pass therefore starts from this already-stably-ordered result.

Lines 51–55Process every digit needed by the maximum
void radixSort(int a[], int n) {
    int max = a[0]; for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i];
    /* Input max is 1,000,000; long long place avoids int exponent overflow. */
    for (long long place = 1; max / place; place *= 10) digitPass(a,n,place);
}

Find the largest accepted value. Starting at place 1, multiply by 10 after each pass while max / place is nonzero. long long safely holds the place progression under this program’s input bound. If every value is zero, no pass is needed.

Lines 56–59Require nonnegative integers and run radix sort
int main(void) {
    int a[MAX_N], n; if (!readArray(a,&n,1)) return invalid();
    radixSort(a,n); printArray(a,n); return 0;
}

Passing 1 to readArray rejects negative values; the digit-index expressions here are designed for nonnegative numbers. Sort, display and return success.

FOLLOW THE VALUES

Dry run

  1. For [170,45,75,90,802,24,2,66], the ones pass gives [170,90,802,2,24,45,75,66].
  2. The tens pass gives [802,2,24,45,66,170,75,90].
  3. The hundreds pass gives [2,24,45,66,75,90,170,802].
  4. Equal digits retain their previous relative order at each pass.

CHECK THE BEHAVIOUR

Sample input & output

These outputs were captured by running the exact C source shown above. Commands use the stated input format; the programs omit repeated menu prompts so the same sample can be pasted and run.

Sample 1

INPUT
8
170 45 75 90 802 24 2 66
OUTPUT
Sorted: 2 24 45 66 75 90 170 802

Sample 2

INPUT
4
0 0 0 0
OUTPUT
Sorted: 0 0 0 0

Sample 3

INPUT
5
1000000 1 10 100 0
OUTPUT
Sorted: 0 1 10 100 1000000

Common mistakes

  • A nonstable digit pass breaks LSD radix sorting.
  • Negative values need a separate design; do not use them as counter indexes.
  • An unrestricted int place *= 10 can overflow; preserve the accepted bounds and wider type.

WHY THIS GROWTH RATE?

Time and space complexity

O(d(n + 10)) time; O(n + 10) auxiliary storage, where d is the maximum decimal digit count.

Each digit pass scans n values to count, ten counters to accumulate, and n values to place/copy. This is O(n + 10) work.

The process repeats for d digit positions, giving O(d(n + 10)). Base ten is fixed, so this is often written O(dn).

If every value is zero, no digit passes are necessary, though reading and finding the maximum still cost O(n). The output array and counters provide the extra storage.

Big-O describes growth in the stated input quantity. The educational limits keep examples safe; complexity discussions state which bounds depend on a generalized input size. Arithmetic on the accepted native numeric types is treated as constant-time.

PREPARE FOR YOUR LAB VIVA

Related viva questions & answers

6 focused questions

Try answering aloud, then expand the answer to check your reasoning. These questions focus on the concepts used in this program.

Why must every digit pass be stable in LSD radix sort?

A later pass groups values by a more significant digit. Within each such group, the earlier less-significant-digit order must survive for the final order to be correct.

Why does the counting placement traverse input from right to left?

Cumulative counts identify the last slot for each digit. Filling those slots backward ensures later equal-digit values remain after earlier ones.

What does (value / place) % 10 extract?

Division discards digits below the chosen place, and remainder by ten keeps the next digit. With place 100, it extracts the hundreds digit.

Why does this program reject negative values rather than automatically sorting them?

The digit-count array has indexes 0 through 9. C remainder for a negative value can be negative, so this representation needs an explicit signed-number extension.

Why is long long used for the place variable?

Successive multiplication by ten can overflow a narrow int in a general digit loop. Together with the input maximum of 1,000,000, the wider variable keeps this example’s exponent updates within range.

What determines d in the radix-sort complexity?

It is the number of decimal places in the largest value, not the number of elements. Increasing the key magnitude can add passes even if n stays unchanged.