Skip to lab content

WEEK 3 · ADVANCED DATA STRUCTURES LAB

Merge sort in ascending order

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

THE SYLLABUS TASK

What you need to solve

Implement comparison-based Merge Sort for a list of integers in ascending order.
Input format & conventions

n (1–100), then n integers between −1,000,000 and 1,000,000. Tokens may be separated by spaces or newlines.

UNDERSTAND THE IDEA

Explanation

Merge sort divides a range into two halves, sorts each half and merges their ordered values. A range containing one element is already sorted.

The merge uses two cursors, one per half. The smaller current value is copied to temporary storage, then any remaining tail is copied.

When values tie, this version takes the value from the left half first. That preserves the original order of equal values, making the implementation stable.

TURN THE IDEA INTO STEPS

Step-by-step algorithm

  1. Read and validate the array.
  2. Stop recursion when low ≥ high.
  3. Split at low + (high−low)/2 and sort both halves.
  4. Merge the halves into the shared temporary array, choosing left on equality.
  5. Copy the merged range back and print the ascending result.

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.

Recursive merge sort

Recursive merge sort

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 merge(int a[], int temp[], int low, int mid, int high) {
    int i = low, j = mid + 1, k = low;
    while (i <= mid && j <= high) temp[k++] = a[i] <= a[j] ? a[i++] : a[j++];
    while (i <= mid) temp[k++] = a[i++];
    while (j <= high) temp[k++] = a[j++];
    for (i = low; i <= high; ++i) a[i] = temp[i];
}
void mergeSort(int a[], int temp[], int low, int high) {
    if (low >= high) return;
    int mid = low + (high - low) / 2;
    mergeSort(a,temp,low,mid); mergeSort(a,temp,mid + 1,high);
    merge(a,temp,low,mid,high);
}
int main(void) {
    int a[MAX_N], temp[MAX_N], n;
    if (!readArray(a,&n,0)) return invalid();
    mergeSort(a,temp,0,n - 1); printArray(a,n); return 0;
}

Local compile: gcc -std=c17 merge-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 to merge two already-sorted subranges
void merge(int a[], int temp[], int low, int mid, int high) {
    int i = low, j = mid + 1, k = low;

i scans low..mid, j scans mid+1..high, and k identifies the next output position in temp. The helper assumes both input subranges have already been sorted.

Line 43Take the smaller front element from the two halves
    while (i <= mid && j <= high) temp[k++] = a[i] <= a[j] ? a[i++] : a[j++];

While both halves have elements, write one into temp and advance its source index. The <= choice takes an equal value from the left half first, preserving stable order for ties.

Lines 44–47Copy the remaining tail and return the merged range
    while (i <= mid) temp[k++] = a[i++];
    while (j <= high) temp[k++] = a[j++];
    for (i = low; i <= high; ++i) a[i] = temp[i];
}

At most one half still contains elements. Copy its remainder, then copy temp[low..high] back into a. The brace ends merge.

Lines 48–50Stop at a single element or split the range
void mergeSort(int a[], int temp[], int low, int high) {
    if (low >= high) return;
    int mid = low + (high - low) / 2;

A range with low >= high needs no sorting. Otherwise calculate a midpoint using the range difference, avoiding direct addition of the two bounds.

Lines 51–53Sort both halves before merging them
    mergeSort(a,temp,low,mid); mergeSort(a,temp,mid + 1,high);
    merge(a,temp,low,mid,high);
}

The recursive calls finish the left and right halves, then merge combines them. Each caller resumes only after its child calls return.

Lines 54–58Read signed values, sort and display them
int main(void) {
    int a[MAX_N], temp[MAX_N], n;
    if (!readArray(a,&n,0)) return invalid();
    mergeSort(a,temp,0,n - 1); printArray(a,n); return 0;
}

main reserves an input and temporary array. Passing 0 to readArray allows negative elements. Sort the used indexes 0..n-1, print the result and return success.

FOLLOW THE VALUES

Dry run

  1. Input [5,2,9,1,3] splits into [5,2,9] and [1,3].
  2. The left side becomes [2,5,9]; the right side remains [1,3].
  3. Merging chooses 1,2,3,5,9 in order.
  4. Each merge writes only its own low…high range in the shared temporary array.

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
5
5 2 9 1 3
OUTPUT
Sorted: 1 2 3 5 9

Sample 2

INPUT
6
3 -2 3 0 -2 1
OUTPUT
Sorted: -2 -2 0 1 3 3

Sample 3

INPUT
1
7
OUTPUT
Sorted: 7

Common mistakes

  • Taking the right element first on equality loses stability.
  • Do not omit the remaining elements when one half is exhausted.
  • Keep copy-back indexes aligned with the original range.

WHY THIS GROWTH RATE?

Time and space complexity

O(n log n) time in best, average and worst cases; O(n) auxiliary buffer plus O(log n) recursion.

At each recursion level, merging all ranges reads and writes O(n) elements in total.

Halving ranges creates O(log n) levels. Multiplying work per level by the number of levels gives O(n log n), including already ordered input.

One temporary array is shared across calls, so buffer storage is O(n); the active recursive path has O(log n) frames.

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 <= rather than < matter in the merge comparison?

It selects the left-half value when two values are equal. Since that value originally preceded the right-half value, their relative order is preserved.

Why can two sorted halves be merged in linear time?

At every comparison, at least one cursor advances. Each element is copied a constant number of times, so merging a range of length k takes O(k) work.

Why is the temporary buffer passed into recursive calls?

The same buffer can serve disjoint merge ranges at different times. Passing it avoids creating a large separate temporary array for every recursive invocation.

What is the merge-sort recursion base case?

A range with zero or one element, represented by low >= high, needs no sorting. Returning there makes the recursive divisions terminate.

Does this merge sort preserve equal values rather than remove duplicates?

Yes. Every element from both halves is copied once, including equal entries. Sorting changes their order while preserving their multiplicities.

Why does merge sort still divide an already sorted array?

This implementation has no check that skips an already ordered range. It follows the same division and merge structure, so the best-case time remains O(n log n).