NivaarExam PrepOfficial exam papers ↗

25-Comp-A4 Program Design and Data Structures · May 2016

Question 7 of 8: File I/O — Merging Two Sorted Files

Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)

Notes on this paper

Paper format. 98-Comp-A4 Program Design and Data Structures, May 2016 — 3 hours, closed book, no calculator permitted. Eight questions of equal weight (20 marks each: some split as (a) 10 + (b) 10); candidates answer any five, so a complete paper is 100 marks. Pseudocode or any high-level language is accepted, and the examiner's note states explicitly that marking emphasises the operation of the program, not syntactic details. All eight questions are answered below, because the whole set is the more useful revision resource. Answers are given in C or C++ as the question dictates; each is compilable as written, but a clear, correctly reasoned pseudocode answer would earn the same marks.

Reference texts for this subject.

  • Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms, 4th ed. — tree traversals (ch. 12), sorting and Quicksort (ch. 7), asymptotic analysis (ch. 3).
  • Weiss, Data Structures and Algorithm Analysis in C, 2nd ed. — linked lists (ch. 3), binary search trees (ch. 4).
  • Deitel & Deitel, C++ How to Program, 10th ed. — class design and templates (ch. 9–12), file streams (ch. 14).
  • Kernighan & Ritchie, The C Programming Language, 2nd ed. — character/array I/O idioms (ch. 1, 7), pointers and structures (ch. 5–6).
  • Stroustrup, The C++ Programming Language, 4th ed. — class templates and value semantics (ch. 3, 25–27).

The Computer Engineering citation list is built around architecture and networking texts (Patterson & Hennessy, Tanenbaum, Mano); this subject is programming and data structures, so the works above are cited instead.

Question 7: File I/O — Merging Two Sorted Files (20 marks)

Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.

Strategy. This is the classic external two-way merge building block: keep one buffered record from each input file "in hand" at all times, repeatedly write out whichever of the two is not lexicographically greater (breaking left/right ties by writing the left one first, which keeps the merge stable), and refill only the file that record came from. When one file runs out of records, the remaining records of the other file are already sorted relative to everything written so far, so they can simply be copied through unchanged. This never needs to hold more than two records in memory regardless of how large the files are.

Given. Two pre-sorted input files, and one output file name, each record up to 80 characters (including the terminating newline) and possibly containing embedded spaces; the two files may have different record counts.

Find. A program that produces one output file containing every record from both inputs, in sorted order.

  1. Buffer exactly one record per input file. A record may contain spaces, so it must be read with fgets (which reads until the newline or the buffer limit), never with scanf("%s") (which stops at whitespace and would silently truncate a multi-word record).
  2. Write the program.
    #include <stdio.h>
    #include <string.h>
    
    #define REC_LEN 81   /* 80 chars + terminating NUL after fgets strips nothing */
    
    int main(void)
    {
        char nameIn1[100], nameIn2[100], nameOut[100];
        char rec1[REC_LEN], rec2[REC_LEN];
        FILE *f1, *f2, *fout;
        int have1, have2;
    
        printf("Enter names of the two input files and the output file: ");
        scanf("%99s %99s %99s", nameIn1, nameIn2, nameOut);
    
        f1 = fopen(nameIn1, "r");
        f2 = fopen(nameIn2, "r");
        fout = fopen(nameOut, "w");
        if (!f1 || !f2 || !fout) {
            printf("Could not open all required files.\n");
            return 1;
        }
    
        have1 = (fgets(rec1, REC_LEN, f1) != NULL);
        have2 = (fgets(rec2, REC_LEN, f2) != NULL);
    
        /* Merge while both files still have a buffered record. */
        while (have1 && have2) {
            if (strcmp(rec1, rec2) <= 0) {       /* rec1 <= rec2: write rec1 (stable on ties) */
                fputs(rec1, fout);
                have1 = (fgets(rec1, REC_LEN, f1) != NULL);
            } else {
                fputs(rec2, fout);
                have2 = (fgets(rec2, REC_LEN, f2) != NULL);
            }
        }
    
        /* Drain whichever file still has records left -- already sorted, so
           just copy the rest through record by record. */
        while (have1) {
            fputs(rec1, fout);
            have1 = (fgets(rec1, REC_LEN, f1) != NULL);
        }
        while (have2) {
            fputs(rec2, fout);
            have2 = (fgets(rec2, REC_LEN, f2) != NULL);
        }
    
        fclose(f1); fclose(f2); fclose(fout);
        return 0;
    }
    Comparing with strcmp directly on the fgets buffers (newline included) is safe: the newline sorts as a normal character and never changes the relative order of two distinct records, since it only ever appears at the very end of each buffer.
  3. Confirm on a small worked example. Merging {apple, cherry, fig, kiwi} with {banana, date, fig, grape, lime} interleaves by comparison at every step, and the trailing drain loop is what correctly appends grape and lime once the first file is exhausted. $$\boxed{\text{merged} = \text{apple, banana, cherry, date, fig, fig, grape, kiwi, lime}}$$
Question 7 — results
QuantityValue
Merged output (worked example)apple, banana, cherry, date, fig, fig, grape, kiwi, lime
Max records held in memory at once2 (one per input file)
Time complexity$O(n_1+n_2)$ — one pass over both files
Either file emptyDrain loop for the other file runs unchanged