Browse papers
A

Section A: Long Answer Questions

Attempt all / any as specified.

4 questions
1long12 marks

(a) Define a stack and explain its LIFO behaviour with the help of PUSH and POP operations. Write the algorithm to convert an infix expression into its equivalent postfix form using a stack. [6]

(b) Using your algorithm (or by tracing a stack), convert the infix expression A + B * (C - D) / E ^ F into postfix, clearly showing the contents of the operator stack at each step. [6]

(a) Stack, LIFO behaviour and Infix-to-Postfix algorithm

Stack: A stack is a linear data structure in which insertion and deletion of elements take place only at one end, called the top. It follows the LIFO (Last-In, First-Out) principle — the element inserted most recently is the first one to be removed.

PUSH adds an element on top of the stack; POP removes the element currently on top. For example, after PUSH(A), PUSH(B), PUSH(C) the stack (top → bottom) is C, B, A; a POP returns C first, then B, then A — confirming LIFO behaviour.

Infix → Postfix algorithm (using an operator stack):

1. Initialise an empty operator stack and an empty output string.
2. Scan the infix expression left to right:
   a. If the token is an operand, append it to the output.
   b. If the token is '(', push it onto the stack.
   c. If the token is ')', pop and output operators until '(' is
      popped (discard the '(').
   d. If the token is an operator o1:
        while the stack top o2 is an operator with
            precedence(o2) > precedence(o1), OR
            precedence(o2) == precedence(o1) and o1 is left-associative,
        pop o2 to the output.
        Then push o1.
3. After scanning, pop all remaining operators to the output.

Precedence: ^ (highest, right-associative) > *, / > +, -.

(b) Converting A + B * (C - D) / E ^ F

TokenActionOperator stack (bottom→top)Output
Aoutput operand(empty)A
+push+A
Boutput operand+A B
*push (*>+)+ *A B
(push+ * (A B
Coutput operand+ * (A B C
-push+ * ( -A B C
Doutput operand+ * ( -A B C D
)pop until (+ *A B C D -
/pop * (same prec, left-assoc), push /+ /A B C D - *
Eoutput operand+ /A B C D - * E
^push (^>/)+ / ^A B C D - * E
Foutput operand+ / ^A B C D - * E F
(end)pop all(empty)A B C D - * E F ^ / +

Postfix: A B C D - * E F ^ / +

stacksexpression-evaluationqueues
2long12 marks

(a) Construct a Binary Search Tree (BST) by inserting the following keys in the given order: 50, 30, 70, 20, 40, 60, 80, 35, 45. Show the resulting tree. [5]

(b) Write down the preorder, inorder and postorder traversal sequences of the BST obtained in (a). [3]

(c) Delete the node with key 30 from the BST and redraw the tree, explaining the deletion strategy used for a node with two children. [4]

(a) Building the BST: 50, 30, 70, 20, 40, 60, 80, 35, 45

Insert each key, going left if smaller than the current node and right if larger:

              50
            /    \
          30      70
         /  \    /  \
       20   40  60   80
           /
          35
            \
             45
  • 50 root; 30<50 → left; 70>50 → right; 20<50,<30 → left of 30; 40<50,>30 → right of 30; 60>50,<70 → left of 70; 80>50,>70 → right of 70; 35<50,>30,<40 → left of 40; 45<50,>30,>40 → right of 40.

(b) Traversals

  • Preorder (Root, Left, Right): 50, 30, 20, 40, 35, 45, 70, 60, 80
  • Inorder (Left, Root, Right): 20, 30, 35, 40, 45, 50, 60, 70, 80 (sorted order)
  • Postorder (Left, Right, Root): 20, 35, 45, 40, 30, 60, 80, 70, 50

(c) Deleting key 30 (a node with two children)

Strategy: When the node to be deleted has two children, replace its key with its inorder successor (smallest key in its right subtree) or inorder predecessor (largest in its left subtree), then delete that successor/predecessor node (which has at most one child).

Node 30 has children 20 (left) and 40 (right). Its inorder successor is the smallest key in the right subtree rooted at 40, which is 35. We copy 35 into the deleted node’s position and remove the original 35 node. The resulting tree:

              50
            /    \
          35      70
         /  \    /  \
       20   40  60   80
              \
               45
trees-bsttree-traversalrecursion
3long12 marks

(a) Differentiate between Breadth First Search (BFS) and Depth First Search (DFS) traversal of a graph, mentioning the auxiliary data structure used by each. [4]

(b) For the weighted, directed graph with vertices {A, B, C, D, E} and edges A→B(4), A→C(1), C→B(2), B→D(1), C→D(5), D→E(3), apply Dijkstra's shortest path algorithm taking A as the source. Show the working table for each iteration and give the shortest distance from A to every other vertex. [8]

(a) BFS vs DFS

AspectBreadth First Search (BFS)Depth First Search (DFS)
ExplorationLevel by level — visits all neighbours of a vertex before going deeperGoes as deep as possible along a branch before backtracking
Auxiliary data structureQueue (FIFO)Stack (LIFO) — explicit or the recursion/call stack
Path found in unweighted graphGives the shortest path (fewest edges)Does not guarantee shortest path
Typical usesShortest path in unweighted graphs, level-order processingCycle detection, topological sort, connected components

Both run in O(V+E)O(V+E) time using an adjacency list.

(b) Dijkstra’s algorithm from source A

Edges: A→B(4), A→C(1), C→B(2), B→D(1), C→D(5), D→E(3).

We maintain tentative distances; each iteration picks the unvisited vertex with the smallest distance and relaxes its outgoing edges.

IterationVertex finalisedABCDE
Init0
1A (0)041
2C (1)0min(4, 1+2)=31min(∞, 1+5)=6
3B (3)031min(6, 3+1)=4
4D (4)0314min(∞, 4+3)=7
5E (7)03147

Shortest distances from A:

  • A → A = 0
  • A → C = 1 (path A–C)
  • A → B = 3 (path A–C–B)
  • A → D = 4 (path A–C–B–D)
  • A → E = 7 (path A–C–B–D–E)
graphsgraph-traversalshortest-path
4long12 marks

(a) Explain the working principle of the Quick Sort algorithm and write its recursive algorithm using a partition procedure. [6]

(b) Sort the array {42, 23, 74, 11, 65, 58, 94, 36, 99, 87} using Quick Sort, showing the array after each partitioning step. [4]

(c) Derive the best-case and worst-case time complexity of Quick Sort. Under what condition does the worst case occur? [2]

(a) Quick Sort — working principle and algorithm

Quick Sort is a divide-and-conquer, in-place sorting algorithm. It chooses a pivot element, then partitions the array so that all elements smaller than the pivot lie to its left and all larger elements to its right — placing the pivot in its final sorted position. It then recursively sorts the two sub-arrays.

QUICKSORT(A, low, high):
    if low < high:
        p = PARTITION(A, low, high)   // pivot in final place
        QUICKSORT(A, low, p - 1)
        QUICKSORT(A, p + 1, high)

PARTITION(A, low, high):           // Lomuto scheme, pivot = A[high]
    pivot = A[high]
    i = low - 1
    for j = low to high - 1:
        if A[j] <= pivot:
            i = i + 1
            swap A[i], A[j]
    swap A[i+1], A[high]
    return i + 1

(b) Sorting {42, 23, 74, 11, 65, 58, 94, 36, 99, 87} (pivot = last element)

Partition 1 (pivot = 87, range 0–9): all of 42,23,74,11,65,58,36 are ≤ 87, while 94 and 99 are > 87. Pivot 87 settles before 94,99. {42, 23, 74, 11, 65, 58, 36, 87, 94, 99} — 87 fixed; left = indices 0–6, right = {94, 99}.

Partition of left {42,23,74,11,65,58,36} (pivot = 36): elements ≤36 are 23,11; pivot lands at position 2. {23, 11, 36, 42, 65, 58, 74} — 36 fixed. Left = {23,11}, right = {42,65,58,74}.

Partition {23,11} (pivot = 11): → {11, 23} — both fixed.

Partition {42,65,58,74} (pivot = 74): all ≤ 74 → {42, 65, 58, 74}, 74 fixed; left = {42,65,58}. Partition {42,65,58} (pivot = 58): 42 ≤ 58 → {42, 58, 65}, 58 fixed.

Partition {94,99} (pivot = 99): → {94, 99}.

Final sorted array: {11, 23, 36, 42, 58, 65, 74, 87, 94, 99}

(c) Time complexity

Let T(n)T(n) be the running time. Partitioning costs O(n)O(n).

  • Best case: the pivot splits the array into two equal halves: T(n)=2T(n/2)+O(n)T(n)=O(nlogn)T(n) = 2T(n/2) + O(n) \Rightarrow T(n) = O(n\log n).
  • Worst case: the pivot is always the smallest or largest element, giving a split of size 00 and n1n-1: T(n)=T(n1)+O(n)T(n)=O(n2)T(n) = T(n-1) + O(n) \Rightarrow T(n) = O(n^2).

The worst case occurs when the array is already sorted (ascending or descending) and the pivot is chosen as the first or last element, producing maximally unbalanced partitions.

sortingalgorithm-complexity
B

Section B: Short Answer Questions

Attempt all / any as specified.

8 questions
5short8 marks

Write an algorithm (or C function) to insert a new node at the end of a singly linked list and another to delete a node having a given key value. Illustrate both operations with suitable pointer diagrams.

Singly linked list — insert at end and delete by key

A node holds data and a next pointer; head points to the first node.

Insert at end

struct Node { int data; struct Node *next; };

void insertEnd(struct Node **head, int value) {
    struct Node *newNode = malloc(sizeof(struct Node));
    newNode->data = value;
    newNode->next = NULL;
    if (*head == NULL) {        // empty list
        *head = newNode;
        return;
    }
    struct Node *p = *head;
    while (p->next != NULL)      // walk to last node
        p = p->next;
    p->next = newNode;           // link new node at the end
}

Pointer diagram (insert X at end):

Before:  head -> [A|*] -> [B|*] -> NULL
After:   head -> [A|*] -> [B|*] -> [X|NULL]
                              ^------------ p->next now points to X

Delete node with a given key

void deleteKey(struct Node **head, int key) {
    struct Node *cur = *head, *prev = NULL;
    if (cur != NULL && cur->data == key) {   // key in first node
        *head = cur->next;
        free(cur);
        return;
    }
    while (cur != NULL && cur->data != key) { // search
        prev = cur;
        cur = cur->next;
    }
    if (cur == NULL) return;                  // key not found
    prev->next = cur->next;                    // bypass the node
    free(cur);
}

Pointer diagram (delete B):

Before:  head -> [A|*] -> [B|*] -> [C|NULL]
                  prev      cur
After:   head -> [A|*] ----------> [C|NULL]      (B freed)
                  prev->next = cur->next
linked-lists
6short8 marks

(a) What is recursion? State the conditions a recursive function must satisfy to terminate correctly. [3]

(b) Write a recursive function to compute the n-th term of the Fibonacci sequence and explain why it is inefficient compared to its iterative version, referring to its time complexity. [5]

(a) Recursion and termination conditions [3]

Recursion is a technique in which a function calls itself, directly or indirectly, to solve a problem by reducing it to smaller instances of the same problem.

For a recursive function to terminate correctly it must satisfy:

  1. Base case — at least one non-recursive condition that stops further calls.
  2. Progress toward the base case — every recursive call must move the argument closer to the base case (e.g. a smaller n).
  3. Correct combination — results of the smaller calls must be combined correctly to give the solution of the original problem.

(b) Recursive Fibonacci and its inefficiency [5]

int fib(int n) {
    if (n <= 1)            // base cases: fib(0)=0, fib(1)=1
        return n;
    return fib(n - 1) + fib(n - 2);
}

Why it is inefficient: The two recursive calls overlap and recompute the same sub-problems repeatedly (e.g. fib(n-2) is evaluated by both fib(n-1) and the second branch). The number of calls grows like the Fibonacci numbers themselves, giving an exponential time complexity of O(φn)O(2n)O(\varphi^n) \approx O(2^n), where φ1.618\varphi \approx 1.618.

The iterative version computes each Fibonacci value once, storing only the previous two:

int fibIter(int n) {
    int a = 0, b = 1, t;
    for (int i = 2; i <= n; i++) { t = a + b; a = b; b = t; }
    return n == 0 ? 0 : b;
}

It runs in O(n)O(n) time and O(1)O(1) space, far more efficient. (The recursive version can also be made O(n)O(n) using memoization.)

recursionalgorithm-complexity
7short8 marks

What is a circular queue and why is it preferred over a linear queue? Write the algorithms for the ENQUEUE and DEQUEUE operations on a circular queue implemented using an array, and state the conditions for queue-full and queue-empty.

Circular queue

A circular queue is a linear queue implemented over a fixed-size array in which the last position is logically connected back to the first, so the array is treated as a circle. The indices front and rear wrap around using modulo arithmetic ((rear + 1) % SIZE).

Why preferred over a linear queue: In a simple linear array queue, once rear reaches the last index, no more elements can be inserted even if the front cells are empty after dequeues — wasting space. A circular queue reuses the freed front cells, achieving full utilisation of the array without shifting elements.

ENQUEUE (insert)

ENQUEUE(Q, item):
    if (rear + 1) % SIZE == front:      // queue full
        report overflow; return
    if front == -1:                      // first insertion
        front = 0
    rear = (rear + 1) % SIZE
    Q[rear] = item

DEQUEUE (remove)

DEQUEUE(Q):
    if front == -1:                      // queue empty
        report underflow; return
    item = Q[front]
    if front == rear:                    // last element removed
        front = rear = -1
    else:
        front = (front + 1) % SIZE
    return item

Conditions (with front/rear initialised to -1)

  • Queue-empty: front == -1
  • Queue-full: (rear + 1) % SIZE == front
queuescircular-queue
8short8 marks

(a) Define hashing and explain what is meant by a collision. [3]

(b) Describe linear probing and chaining as collision-resolution techniques, clearly stating one advantage and one disadvantage of each. [5]

(a) Hashing and collision [3]

Hashing is a technique that maps a key to an index (the home address) in a fixed-size table using a hash function h(key)h(key), allowing search, insertion and deletion in average O(1)O(1) time.

A collision occurs when two distinct keys hash to the same table index, i.e. h(k1)=h(k2)h(k_1) = h(k_2) with k1k2k_1 \ne k_2. Because a finite table cannot give every possible key a unique slot, collisions are unavoidable and must be resolved.

(b) Collision-resolution techniques [5]

Linear probing (open addressing): On a collision, probe the next slots sequentially — h(k),h(k)+1,h(k)+2,h(k), h(k)+1, h(k)+2, \dots (mod table size) — until an empty slot is found.

  • Advantage: simple; uses no extra pointers, and good cache locality.
  • Disadvantage: suffers from primary clustering — long runs of occupied slots form, degrading performance as the load factor rises; the table can also become full.

Chaining (separate chaining): Each table slot holds the head of a linked list; all keys hashing to that slot are stored in its list.

  • Advantage: the table never “fills up” — it handles any number of collisions gracefully and deletion is simple.
  • Disadvantage: needs extra memory for pointers, and performance degrades to O(n)O(n) if many keys map to one chain; loses cache locality.
Linear probingChaining
Storagewithin tableexternal linked lists
Table-full possible?yesno
Main weaknessclusteringpointer overhead
searching-hashinghashing
9short8 marks

What is an AVL tree? Insert the keys 30, 20, 40, 10, 25, 22 one by one into an initially empty AVL tree, showing the balance factors and naming the rotation performed at each point where rebalancing is required.

AVL tree

An AVL tree is a self-balancing binary search tree in which, for every node, the balance factor = height(left subtree) − height(right subtree) is restricted to −1, 0, or +1. Whenever an insertion/deletion violates this, a rotation (LL, RR, LR or RL) restores balance, keeping the height O(logn)O(\log n).

Inserting 30, 20, 40, 10, 25, 22

Insert 30: root. BF(30)=0.

30

Insert 20: left of 30. BF(30)=+1 — balanced.

  30
 /
20

Insert 40: right of 30. BF(30)=0 — balanced.

  30
 /  \
20   40

Insert 10: left of 20. BF(20)=+1, BF(30)=+1 — still balanced.

     30
    /  \
   20   40
  /
 10

Insert 25: right of 20. BF(20)=0, BF(30)=+1 — balanced.

     30
    /  \
   20   40
  /  \
 10   25

Insert 22: goes left of 25 (22<30, >20, <25). Now BF(25)=+1, BF(20)=−1, BF(30)=+2 — unbalanced. The imbalance at node 30 is Left-Right (LR): inserted in the right subtree (25) of 30’s left child (20). Perform an LR rotation (left-rotate 20, then right-rotate 30), bringing 25 up:

        25
       /  \
      20   30
     /  \    \
    10   22   40

All balance factors are now in {−1, 0, +1}; the tree is balanced.

Summary of rotations: Only one rebalancing was needed — an LR rotation at node 30 after inserting 22.

trees-bstavl-tree
10short8 marks

(a) Define Big-O, Big-Omega (Ω) and Big-Theta (Θ) asymptotic notations. [4]

(b) Determine the time complexity of the following code fragment in terms of Big-O notation, justifying your answer:

for (i = 1; i <= n; i++)
    for (j = 1; j <= n; j = j * 2)
        x = x + 1;

[4]

(a) Asymptotic notations [4]

For running time f(n)f(n):

  • Big-O (upper bound): f(n)=O(g(n))f(n) = O(g(n)) if c>0,n0\exists\, c>0, n_0 such that 0f(n)cg(n)0 \le f(n) \le c\,g(n) for all nn0n \ge n_0. It bounds the worst-case growth from above.
  • Big-Omega (Ω\Omega, lower bound): f(n)=Ω(g(n))f(n) = \Omega(g(n)) if c>0,n0\exists\, c>0, n_0 such that 0cg(n)f(n)0 \le c\,g(n) \le f(n) for all nn0n \ge n_0. It bounds growth from below (best case).
  • Big-Theta (Θ\Theta, tight bound): f(n)=Θ(g(n))f(n) = \Theta(g(n)) if f(n)=O(g(n))f(n)=O(g(n)) and f(n)=Ω(g(n))f(n)=\Omega(g(n)), i.e. c1g(n)f(n)c2g(n)c_1 g(n) \le f(n) \le c_2 g(n). It means ff grows exactly like gg.

(b) Complexity of the code fragment [4]

for (i = 1; i <= n; i++)          // outer loop
    for (j = 1; j <= n; j = j*2)  // inner loop: j doubles
        x = x + 1;
  • The outer loop runs nn times.
  • In the inner loop, j starts at 1 and doubles each iteration (1, 2, 4, 8, …) until it exceeds nn. The number of iterations is the number of doublings to reach nn, which is log2n+1=Θ(logn)\lfloor \log_2 n \rfloor + 1 = \Theta(\log n).

Total iterations =n×log2n= n \times \log_2 n.

Time complexity = O(nlogn)O(n \log n).

algorithm-complexityasymptotic-notation
11short8 marks

(a) Write the algorithm for binary search on a sorted array and explain why the array must be sorted. [4]

(b) Compare linear search and binary search in terms of their average-case and worst-case time complexities. [4]

(a) Binary search algorithm [4]

int binarySearch(int A[], int n, int key) {
    int low = 0, high = n - 1;
    while (low <= high) {
        int mid = low + (high - low) / 2;
        if (A[mid] == key)      return mid;     // found
        else if (A[mid] < key)  low  = mid + 1;  // search right half
        else                    high = mid - 1;  // search left half
    }
    return -1;                                   // not found
}

Why the array must be sorted: Binary search decides which half to discard by comparing the key with the middle element. This is only valid if all elements to the left of mid are smaller and all to the right are larger — i.e. the array is sorted. On an unsorted array the discarded half might still contain the key, so the result would be wrong.

(b) Linear search vs Binary search [4]

Linear searchBinary search
Data requirementworks on unsorted datarequires sorted data
Strategycheck elements one by onehalve the search range each step
Average caseO(n)O(n)O(logn)O(\log n)
Worst caseO(n)O(n)O(logn)O(\log n)
Best caseO(1)O(1)O(1)O(1)

Binary search is far faster on large sorted arrays because the search space is halved at every comparison, giving logarithmic time, whereas linear search may scan the whole array.

searching-hashingsorting
12short8 marks

Explain how a stack can be implemented using a linked list. Write the push and pop operations for such an implementation and state one advantage of a linked-list-based stack over an array-based stack.

Stack using a linked list

A stack can be implemented with a singly linked list where the top pointer plays the role of the stack top and points to the most recently inserted node. Both push and pop operate at the head of the list, so each takes O(1)O(1) time and the stack grows/shrinks dynamically without a fixed size.

struct Node { int data; struct Node *next; };
struct Node *top = NULL;        // empty stack

Push (insert at head)

void push(int value) {
    struct Node *newNode = malloc(sizeof(struct Node));
    newNode->data = value;
    newNode->next = top;        // new node points to old top
    top = newNode;              // top now points to new node
}

Pop (remove from head)

int pop() {
    if (top == NULL) {          // underflow
        printf("Stack empty\n");
        return -1;
    }
    struct Node *temp = top;
    int value = temp->data;
    top = top->next;            // move top to next node
    free(temp);
    return value;
}

Pointer diagram (push X):

Before: top -> [B|*] -> [A|NULL]
After:  top -> [X|*] -> [B|*] -> [A|NULL]

Advantage over an array-based stack

A linked-list stack has no fixed capacity — it grows and shrinks dynamically at run time, so there is no overflow as long as memory is available (and no wasted pre-allocated space), whereas an array-based stack is limited to a predefined size.

linked-listsstacks

Frequently asked questions

Where can I find the BE Computer Engineering (IOE, TU) Data Structure and Algorithm (IOE, CT 552) question paper 2079?
The full BE Computer Engineering (IOE, TU) Data Structure and Algorithm (IOE, CT 552) 2079 (regular) question paper is available free on Kekkei. You can read every question online and attempt the paper under timed exam conditions.
Does the Data Structure and Algorithm (IOE, CT 552) 2079 paper come with solutions?
Yes. Every question on this Data Structure and Algorithm (IOE, CT 552) past paper includes a step-by-step solution, plus instant AI feedback when you attempt it on Kekkei.
How many marks is the BE Computer Engineering (IOE, TU) Data Structure and Algorithm (IOE, CT 552) 2079 paper?
The BE Computer Engineering (IOE, TU) Data Structure and Algorithm (IOE, CT 552) 2079 paper carries 80 full marks and is meant to be completed in 180 minutes, across 12 questions.
Is practising this Data Structure and Algorithm (IOE, CT 552) past paper free?
Yes — reading and attempting this Data Structure and Algorithm (IOE, CT 552) past paper on Kekkei is completely free.