Skip to lab content

WEEK 4 · ADVANCED DATA STRUCTURES LAB

Bucket sort — distribute, sort and combine

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

THE SYLLABUS TASK

What you need to solve

Implement Bucket Sort for a list of integers in ascending order.
Input format & conventions

n (1–100), then n nonnegative integers up to 1,000,000. Ten buckets are chosen from the data range; each bucket uses stable insertion sort.

UNDERSTAND THE IDEA

Explanation

Bucket sort groups values into ordered numeric ranges. If a smaller bucket index always represents a lower range, concatenating sorted buckets produces global sorted order.

Distribution alone is not enough. Values arriving in the same bucket can still be unordered, which is the main error in the supplied manual.

This version maps value v to floor(v×10/(max+1)). The +1 prevents division by zero for all-zero input and keeps the largest value below bucket index ten. Every bucket can hold all 100 accepted elements.

TURN THE IDEA INTO STEPS

Step-by-step algorithm

  1. Find the maximum and clear ten bucket counts.
  2. Compute a safe bucket index for each value and append it there.
  3. Insertion-sort the occupied entries within every bucket.
  4. Visit bucket indexes 0 through 9 and copy their sorted values back.
  5. Print the complete ascending list.

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.

Distribution and within-bucket sorting

Distribution and within-bucket sorting

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');
}

#define BUCKETS 10
void bucketSort(int a[], int n) {
    int buckets[BUCKETS][MAX_N], count[BUCKETS] = {0}, max = a[0];
    for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i];
    for (int i = 0; i < n; ++i) {
        int b = (int)((long long)a[i] * BUCKETS / ((long long)max + 1));
        buckets[b][count[b]++] = a[i];
    }
    int out = 0;
    for (int b = 0; b < BUCKETS; ++b) {
        /* Stable insertion sort is essential: distribution alone is not sorting. */
        for (int i = 1; i < count[b]; ++i) {
            int key = buckets[b][i], j = i - 1;
            while (j >= 0 && buckets[b][j] > key) {
                buckets[b][j + 1] = buckets[b][j]; --j;
            }
            buckets[b][j + 1] = key;
        }
        for (int i = 0; i < count[b]; ++i) a[out++] = buckets[b][i];
    }
}
int main(void) {
    int a[MAX_N], n; if (!readArray(a,&n,1)) return invalid();
    bucketSort(a,n); printArray(a,n); return 0;
}

Local compile: gcc -std=c17 bucket-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–44Create ten buckets and find the maximum value
#define BUCKETS 10
void bucketSort(int a[], int n) {
    int buckets[BUCKETS][MAX_N], count[BUCKETS] = {0}, max = a[0];
    for (int i = 1; i < n; ++i) if (a[i] > max) max = a[i];

Each bucket row can hold up to MAX_N items. The zeroed count array tracks used slots in each bucket. Find max to scale the nonnegative inputs across the bucket indexes.

Lines 45–48Distribute each value into a bounded bucket
    for (int i = 0; i < n; ++i) {
        int b = (int)((long long)a[i] * BUCKETS / ((long long)max + 1));
        buckets[b][count[b]++] = a[i];
    }

Compute floor(value × 10 / (max + 1)). Using max + 1 keeps even the largest value below bucket index 10; it is also positive for all-zero input. Cast before multiplication to use wider arithmetic. Store into the bucket’s next slot and increase its count.

Lines 49–53Prepare each bucket for an internal insertion sort
    int out = 0;
    for (int b = 0; b < BUCKETS; ++b) {
        /* Stable insertion sort is essential: distribution alone is not sorting. */
        for (int i = 1; i < count[b]; ++i) {
            int key = buckets[b][i], j = i - 1;

out starts at zero for final output. Visit buckets in increasing index order. Within a bucket, take each later element as key and begin checking its left neighbour.

Lines 54–58Shift larger bucket elements and insert the saved key
            while (j >= 0 && buckets[b][j] > key) {
                buckets[b][j + 1] = buckets[b][j]; --j;
            }
            buckets[b][j + 1] = key;
        }

Check j >= 0 before accessing the left element. Shift larger values right until the key’s position is found, then write key into j + 1. Distribution alone does not sort values that share one bucket.

Lines 59–61Concatenate the sorted buckets in index order
        for (int i = 0; i < count[b]; ++i) a[out++] = buckets[b][i];
    }
}

Copy each used bucket item into a[out++] before moving to the next bucket. The scaling order between buckets and the sort within each bucket together produce ascending output.

Lines 62–65Accept nonnegative input and run the complete sort
int main(void) {
    int a[MAX_N], n; if (!readArray(a,&n,1)) return invalid();
    bucketSort(a,n); printArray(a,n); return 0;
}

Read at least one and at most 100 nonnegative integers, call bucketSort, display the updated array and return success.

FOLLOW THE VALUES

Dry run

  1. For [29,25,3,49,9,37,21,43], max+1 is 50.
  2. Values 29 and 25 enter bucket 5; their arrival order is not sorted. Value 21 enters bucket 4.
  3. Sorting bucket 5 changes it to [25,29].
  4. Combining all sorted buckets gives [3,9,21,25,29,37,43,49].

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
29 25 3 49 9 37 21 43
OUTPUT
Sorted: 3 9 21 25 29 37 43 49

Sample 2

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

Sample 3

INPUT
6
100 100 100 99 100 98
OUTPUT
Sorted: 98 99 100 100 100 100

Common mistakes

  • Do not concatenate unsorted bucket contents.
  • Do not assume a bucket contains at most ten values.
  • This method uses comparisons inside buckets even though its distribution stage is categorized as non-comparison sorting.

WHY THIS GROWTH RATE?

Time and space complexity

O(n + b + Σ nᵢ²) for insertion-sorted buckets; worst O(n² + b). This code fixes b = 10.

Distribution and gathering are O(n + b). Insertion sorting bucket i can cost O(nᵢ²), so all bucket sorts contribute their sum.

Uniform distribution gives expected O(n + b + n²/b) under the usual occupancy model. Expected linear time needs b proportional to n; ten fixed buckets do not justify an unconditional O(n) claim as n grows.

All values can land in one bucket, producing quadratic insertion-sort work. The sample reserves b×MAX_N integers; a similarly sized rectangular allocation is O(bn), or O(n) with fixed b.

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 does the manual’s bucket program fail on 29, 25 and 21?

All three enter the same tens bucket and are copied back in arrival order. Without sorting that bucket, their output remains 29,25,21 instead of ascending.

Why is max + 1 used in the range-mapping denominator?

It keeps max × 10 / (max + 1) below 10 and remains nonzero even when max is zero. The resulting indexes are always within the ten buckets.

Can every input value legally land in one bucket here?

Yes. Distribution can be strongly skewed. Each bucket reserves enough space for the entire accepted input so that this case does not overflow storage.

Why does bucket concatenation need increasing bucket indexes?

The index mapping orders value ranges. Traversing buckets in increasing order places lower ranges before higher ranges once each individual bucket is sorted.

Is this bucket sort completely free from comparisons?

No. Bucket selection uses arithmetic, but insertion sort inside each bucket compares values. The syllabus category describes the distribution strategy, not an absence of all comparisons.

When would the familiar expected-linear bucket bound apply?

With a suitable distribution and a bucket count proportional to n, expected bucket occupancy stays bounded. This ten-bucket example explicitly states a more general bound instead of promising linear time.