BE Computer Engineering (Pokhara University) Data Structure and Algorithms (PU, CMP 160) Question Paper 2079 Nepal
This is the official BE Computer Engineering (Pokhara University) Data Structure and Algorithms (PU, CMP 160) question paper for 2079, as set in the regular annual examination. It carries 100 full marks and a time allowance of 180 minutes, across 12 questions. On Kekkei you can attempt this Data Structure and Algorithms (PU, CMP 160) past paper online with a timer, get instant AI feedback and step-by-step solutions, and track the topics where you lose marks — completely free. Whether you are revising for your BE Computer Engineering (Pokhara University) Data Structure and Algorithms (PU, CMP 160) exam or solving previous years' question papers, this 2079 paper is a great way to practise under real exam conditions.
Section A: Long Answer Questions
Attempt all / any as specified.
(a) Define asymptotic notations. Explain Big-O, Big-Omega () and Big-Theta () notations with the help of suitable graphs and examples. (8)
(b) Compute the worst-case and best-case running time of the following code segment in terms of Big-O notation, clearly showing the frequency count of each statement: (6)
for (i = 0; i < n; i++) {
for (j = i; j < n; j++) {
sum = sum + a[i][j];
}
}
(a) Asymptotic Notations
Asymptotic notations describe the limiting behaviour of an algorithm's running time as the input size grows large. They let us compare algorithms independent of machine and constants.
Big-O (Upper Bound)
if there exist constants and such that
It gives the worst-case (upper) bound. Example: .
Graph: lies above for all .
Big-Omega () (Lower Bound)
if there exist and such that
It gives the best-case (lower) bound. Example: .
Graph: lies below for all .
Big-Theta () (Tight Bound)
if there exist and such that
It squeezes between two multiples of (both upper and lower bound). Example: .
Graph: is sandwiched between and for all .
(b) Frequency Count of the Code
for (i = 0; i < n; i++) { // outer loop
for (j = i; j < n; j++) { // inner loop
sum = sum + a[i][j]; // body
}
}
The inner loop runs times for each value of . Total executions of the body:
Frequency count of the body statement .
Since there is no early exit (no conditional break), the number of iterations is the same for every input. Therefore:
- Best case:
- Worst case:
The code is always .
(a) What is an AVL tree? Construct an AVL tree by inserting the following keys one by one in the given order, showing the rotation performed (LL, RR, LR or RL) at each step where rebalancing is required: 30, 31, 32, 23, 22, 24, 16, 17, 18. (10)
(b) Differentiate between a binary tree and a binary search tree. Write down the in-order, pre-order and post-order traversal sequences of the final AVL tree obtained in part (a). (4)
(a) AVL Tree
An AVL tree is a self-balancing binary search tree in which, for every node, the balance factor (height of left subtree height of right subtree) is , , or . When an insertion violates this, a rotation (LL, RR, LR, RL) restores balance.
Insertion of 30, 31, 32, 23, 22, 24, 16, 17, 18
- Insert 30 → root
30. - Insert 31 → right child of 30. Balanced.
- Insert 32 → unbalanced at 30 (BF , right-right). RR rotation →
31becomes root with children30,32. - Insert 23 → goes left of 30. Balanced.
- Insert 22 → unbalanced at 30 (left-left). LL rotation at 30 →
23with children22,30. Tree: root 31, left 23 (children 22, 30), right 32. - Insert 24 → right child of 23... it goes left of 30. Balanced (no violation).
- Insert 16 → left of 22. Unbalanced at 31 (BF , left subtree heavy, left-left). LL rotation at 31 →
23becomes new root. Left child 22 (with child 16), right child 31 (with left 30 containing 24, right 32). - Insert 17 → unbalanced at 22 (left-right: 16 then right 17). LR rotation → 17 becomes parent of 16 and 22.
- Insert 18 → unbalanced at 23 root region (left-right path 17→18). RL/LR rotation restores balance; 18 settles under 17.
Final balanced AVL tree
23
/ \
17 31
/ \ / \
16 22 30 32
\ /
18 24
(Heights satisfy |BF| ≤ 1 at every node.)
(b) Binary Tree vs Binary Search Tree
| Binary Tree | Binary Search Tree (BST) |
|---|---|
| Each node has at most 2 children; no ordering rule. | Left subtree keys < node < right subtree keys. |
| No restriction on values. | Strict ordering of keys. |
| Searching is . | Searching is (balanced). |
| In-order traversal gives no special order. | In-order traversal gives sorted order. |
Traversals of the final tree
- In-order (L, Root, R): 16, 17, 18, 22, 23, 24, 30, 31, 32 (sorted)
- Pre-order (Root, L, R): 23, 17, 16, 22, 18, 31, 30, 24, 32
- Post-order (L, R, Root): 16, 18, 22, 17, 24, 30, 32, 31, 23
(a) Consider a weighted, undirected graph with vertices {A, B, C, D, E} and the following edges with weights: A-B(4), A-C(1), C-B(2), B-D(5), C-D(8), C-E(10), D-E(2). Represent this graph using an adjacency matrix. (4)
(b) Apply Dijkstra's shortest path algorithm to find the shortest path from source vertex A to all other vertices. Show the contents of the distance table at every iteration. (8)
(a) Adjacency Matrix
Vertices {A, B, C, D, E}, undirected edges: A-B(4), A-C(1), C-B(2), B-D(5), C-D(8), C-E(10), D-E(2). Entry = weight, (or 0) if no edge; matrix is symmetric.
| A | B | C | D | E | |
|---|---|---|---|---|---|
| A | 0 | 4 | 1 | ||
| B | 4 | 0 | 2 | 5 | |
| C | 1 | 2 | 0 | 8 | 10 |
| D | 5 | 8 | 0 | 2 | |
| E | 10 | 2 | 0 |
(b) Dijkstra's Algorithm from source A
Initialize , others . At each iteration pick the unvisited vertex with minimum distance and relax its neighbours.
| Iteration | Visited | A | B | C | D | E |
|---|---|---|---|---|---|---|
| Init | – | 0 | ∞ | ∞ | ∞ | ∞ |
| 1: pick A (0) | {A} | 0 | 4 | 1 | ∞ | ∞ |
| 2: pick C (1) | {A,C} | 0 | 3 (1+2) | 1 | 9 (1+8) | 11 (1+10) |
| 3: pick B (3) | {A,C,B} | 0 | 3 | 1 | 8 (3+5) | 11 |
| 4: pick D (8) | {A,C,B,D} | 0 | 3 | 1 | 8 | 10 (8+2) |
| 5: pick E (10) | all | 0 | 3 | 1 | 8 | 10 |
Final shortest distances and paths from A
- A → A: 0
- A → C: 1 (A→C)
- A → B: 3 (A→C→B)
- A → D: 8 (A→C→B→D)
- A → E: 10 (A→C→B→D→E)
(a) Convert the following infix expression into its equivalent postfix expression using a stack, showing the status of the stack and output at each scanned symbol: A + B * (C - D) / E ^ F. (6)
(b) Explain how recursion is implemented internally using the system stack. Write a recursive function to compute the factorial of a non-negative integer and trace its execution for n = 4. (4)
(a) Infix → Postfix: A + B * (C - D) / E ^ F
Precedence: ^ (highest, right-assoc) > *,/ > +,-. Scan left to right; operands go to output, operators are pushed respecting precedence.
| Symbol | Stack | Output |
|---|---|---|
| A | (empty) | A |
| + | + | A |
| B | + | A B |
| * | + * | A B |
| ( | + * ( | A B |
| C | + * ( | A B C |
| - | + * ( - | A B C |
| D | + * ( - | A B C D |
| ) | + * | A B C D - |
| / | + / | A B C D - * |
| E | + / | A B C D - * E |
| ^ | + / ^ | A B C D - * E |
| F | + / ^ | A B C D - * E F |
| end | (pop all) | A B C D - * E F ^ / + |
Postfix: A B C D - * E F ^ / +
(b) Recursion and the System Stack
When a function calls itself, the system pushes an activation record (stack frame) onto the call stack containing the return address, parameters, and local variables. Each recursive call adds a new frame; when a call returns, its frame is popped and control returns to the caller. The base case stops the recursion and unwinding begins.
int factorial(int n) {
if (n == 0) // base case
return 1;
return n * factorial(n - 1); // recursive case
}
Trace for n = 4
factorial(4) = 4 * factorial(3)
factorial(3) = 3 * factorial(2)
factorial(2) = 2 * factorial(1)
factorial(1) = 1 * factorial(0)
factorial(0) = 1 <- base case, stack unwinds
=> factorial(1) = 1*1 = 1
=> factorial(2) = 2*1 = 2
=> factorial(3) = 3*2 = 6
=> factorial(4) = 4*6 = 24
Result: 4! = 24.
Section B: Short Answer Questions
Attempt all / any as specified.
Write an algorithm (or C function) to insert a node at the beginning, at the end, and after a given node in a singly linked list. State one advantage of a linked list over an array.
Insertion in a Singly Linked List
struct Node { int data; struct Node *next; };
// 1. Insert at the BEGINNING
struct Node* insertBegin(struct Node *head, int x) {
struct Node *n = malloc(sizeof(struct Node));
n->data = x;
n->next = head; // point to old head
return n; // new head
}
// 2. Insert at the END
struct Node* insertEnd(struct Node *head, int x) {
struct Node *n = malloc(sizeof(struct Node));
n->data = x; n->next = NULL;
if (head == NULL) return n;
struct Node *t = head;
while (t->next != NULL) t = t->next; // go to last node
t->next = n;
return head;
}
// 3. Insert AFTER a given node p
void insertAfter(struct Node *p, int x) {
if (p == NULL) return;
struct Node *n = malloc(sizeof(struct Node));
n->data = x;
n->next = p->next; // link new node to p's successor
p->next = n; // link p to new node
}
Advantage of a linked list over an array: A linked list is dynamic — it can grow or shrink at run time without a fixed size, and insertion/deletion at the front (or after a known node) takes time without shifting elements, whereas an array requires shifting and has fixed capacity.
Insert the keys 72, 27, 36, 24, 63, 81, 92 into a hash table of size 10 using the hash function . Resolve collisions using quadratic probing. Show the final contents of the hash table.
Hashing with Quadratic Probing
Table size , . On collision, probe positions for
Insert 72, 27, 36, 24, 63, 81, 92:
- 72 → . Slot 2 empty → place at 2.
- 27 → 7. Empty → 7.
- 36 → 6. Empty → 6.
- 24 → 4. Empty → 4.
- 63 → 3. Empty → 3.
- 81 → 1. Empty → 1.
- 92 → 2 (occupied by 72). Probe : (occupied by 63). Probe : (occupied by 36). Probe : (occupied by 81). Probe : (empty) → place at 8.
Final Hash Table
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Key | – | 81 | 72 | 63 | 24 | – | 36 | 27 | 92 | – |
Sort the following list using quick sort, taking the first element as the pivot. Show the array after each partitioning step: 44, 33, 11, 55, 77, 90, 40, 60, 99, 22, 88. State the worst-case time complexity of quick sort and the input condition that causes it.
Quick Sort (first element as pivot)
List: 44, 33, 11, 55, 77, 90, 40, 60, 99, 22, 88
Pass 1 — pivot = 44. Partition so elements < 44 go left, > 44 go right. 44 settles into its sorted position:
[33, 11, 40, 22] 44 [77, 90, 55, 60, 99, 88]
Left sublist [33, 11, 40, 22], pivot = 33:
[11, 22] 33 [40] → gives [11, 22, 33, 40]
- sub
[11, 22], pivot 11 →11 [22]→[11, 22].
Right sublist [77, 90, 55, 60, 99, 88], pivot = 77:
[55, 60] 77 [90, 99, 88]
[55, 60], pivot 55 →55 [60]→[55, 60].[90, 99, 88], pivot 90 →[88] 90 [99]→[88, 90, 99].
Combine: 11, 22, 33, 40, 44, 55, 60, 77, 88, 90, 99
Sorted array
11, 22, 33, 40, 44, 55, 60, 77, 88, 90, 99
Worst-case complexity
The worst case of quick sort is . It occurs when the pivot is always the smallest or largest element, producing maximally unbalanced partitions (one side empty). With first-element pivot this happens when the input is already sorted (ascending or descending).
What is a circular queue? Explain how it overcomes the limitation of a linear queue. Write the conditions to detect queue-full and queue-empty states in a circular queue implemented using an array.
Circular Queue
A circular queue is a linear data structure in which the last position is logically connected back to the first position, forming a circle. The array indices wrap around using modulo arithmetic: after the last index, the next index becomes 0.
How it overcomes the linear-queue limitation
In a simple linear queue implemented with an array, after several enqueue/dequeue operations the front and rear pointers keep moving forward. Even when the front portion of the array is empty, the rear may reach the end and the queue is wrongly reported as full — this wastes space (false overflow). A circular queue reuses the vacated front slots by wrapping rear (and front) around with , so all array cells can be used.
Full and Empty conditions (array of size SIZE)
Using front and rear initialized to :
- Queue Empty:
front == -1 - Queue Full:
(rear + 1) % SIZE == front
Enqueue: rear = (rear + 1) % SIZE. Dequeue: if front == rear set both to , else front = (front + 1) % SIZE.
Explain radix sort with a suitable example. Sort the list 170, 45, 75, 90, 802, 24, 2, 66 using radix sort and show the buckets/passes. Compare its complexity with comparison-based sorting algorithms.
Radix Sort
Radix sort is a non-comparison, integer sorting algorithm that sorts numbers digit by digit, from the least significant digit (LSD) to the most significant digit. In each pass it distributes elements into 10 buckets (0–9) based on the current digit using a stable sort (counting sort), then collects them back.
Sorting: 170, 45, 75, 90, 802, 24, 2, 66
Pass 1 — units digit:
| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Keys | 170, 90 | 802, 2 | 24 | 45, 75 | 66 |
Collect → 170, 90, 802, 2, 24, 45, 75, 66
Pass 2 — tens digit:
| Bucket | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Keys | 802, 2 | 24 | 45 | 66 | 170, 75 | 90 |
Collect → 802, 2, 24, 45, 66, 170, 75, 90
Pass 3 — hundreds digit:
| Bucket | 0 | 1 | 2 | ... | 8 |
|---|---|---|---|---|---|
| Keys | 2, 24, 45, 66, 75, 90 | 170 | 802 |
Collect → 2, 24, 45, 66, 75, 90, 170, 802
Sorted output
2, 24, 45, 66, 75, 90, 170, 802
Complexity comparison
Radix sort runs in where = number of digits and = base (10). For fixed-width integers this is effectively linear time, beating the lower bound of comparison-based sorts (merge sort, quick sort, heap sort). The trade-off is extra space and that it works only for integers/fixed-length keys.
Differentiate between Breadth First Search (BFS) and Depth First Search (DFS) traversal of a graph. For the graph having edges {(1,2),(1,3),(2,4),(3,4),(4,5),(3,5)}, write the BFS and DFS traversal order starting from vertex 1.
BFS vs DFS
| Breadth First Search (BFS) | Depth First Search (DFS) |
|---|---|
| Explores level by level (all neighbours first). | Explores as deep as possible along a branch before backtracking. |
| Uses a queue (FIFO). | Uses a stack / recursion (LIFO). |
| Finds shortest path (fewest edges) in unweighted graphs. | Does not guarantee shortest path. |
| Higher memory (stores whole frontier). | Lower memory along a single path. |
| Time: . | Time: . |
Traversal from vertex 1
Edges: (1,2),(1,3),(2,4),(3,4),(4,5),(3,5). Adjacency (ascending order):
- 1 → 2, 3
- 2 → 1, 4
- 3 → 1, 4, 5
- 4 → 2, 3, 5
- 5 → 3, 4
BFS (queue, visit neighbours in ascending order): Start 1 → visit 2, 3 → from 2 visit 4 → from 3 visit 5. BFS order: 1, 2, 3, 4, 5
DFS (stack/recursion, ascending order): 1 → 2 → 4 → 3 → 5. (1, go to 2; from 2 go to 4; from 4 go to 3; from 3 go to 5.) DFS order: 1, 2, 4, 3, 5
Construct a binary search tree (BST) by inserting the keys 50, 30, 70, 20, 40, 60, 80, 10 in the given order. Then show the resulting tree after deleting the node with key 30, explaining the deletion case applied.
Building and Deleting in a BST
Insert 50, 30, 70, 20, 40, 60, 80, 10
Applying the BST rule (smaller left, larger right):
50
/ \
30 70
/ \ / \
20 40 60 80
/
10
Delete node with key 30
Node 30 has two children (20 and 40). This is the deletion case for a node with two children: replace the node with its in-order successor (smallest key in the right subtree) — here the successor is 40 — then remove that successor node.
Replace 30 with 40; node 40 (a leaf) is deleted from its old place. Resulting tree:
50
/ \
40 70
/ / \
20 60 80
/
10
(Alternatively, the in-order predecessor 20 could be used; both are valid. In-order traversal of the result — 10, 20, 40, 50, 60, 70, 80 — remains sorted, confirming correctness.)
Write a recursive algorithm for the Tower of Hanoi problem with n disks. Derive its recurrence relation and solve it to show that the number of moves required is .
Tower of Hanoi
Move disks from source peg A to destination peg C using auxiliary peg B, never placing a larger disk on a smaller one.
Recursive algorithm
void hanoi(int n, char A, char C, char B) {
if (n == 1) { // base case
printf("Move disk 1 from %c to %c\n", A, C);
return;
}
hanoi(n - 1, A, B, C); // 1. move top n-1 from A to B
printf("Move disk %d from %c to %c\n", n, A, C); // 2. move largest to C
hanoi(n - 1, B, C, A); // 3. move n-1 from B to C
}
Recurrence relation
Let be the number of moves for disks. Moving disks twice plus one move for the largest disk:
Solving by back-substitution
(Using and the geometric sum .)
Result
So disks require moves, giving time complexity .
Frequently asked questions
- Where can I find the BE Computer Engineering (Pokhara University) Data Structure and Algorithms (PU, CMP 160) question paper 2079?
- The full BE Computer Engineering (Pokhara University) Data Structure and Algorithms (PU, CMP 160) 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 Algorithms (PU, CMP 160) 2079 paper come with solutions?
- Yes. Every question on this Data Structure and Algorithms (PU, CMP 160) 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 (Pokhara University) Data Structure and Algorithms (PU, CMP 160) 2079 paper?
- The BE Computer Engineering (Pokhara University) Data Structure and Algorithms (PU, CMP 160) 2079 paper carries 100 full marks and is meant to be completed in 180 minutes, across 12 questions.
- Is practising this Data Structure and Algorithms (PU, CMP 160) past paper free?
- Yes — reading and attempting this Data Structure and Algorithms (PU, CMP 160) past paper on Kekkei is completely free.