0% found this document useful (0 votes)
9 views13 pages

Assignment 2 DSA

Assignment
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views13 pages

Assignment 2 DSA

Assignment
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

Ans 1- Recursion can be removed from a program by using iterative techniques like loops.

For
example, calculating factorial can be done recursively or iteratively. The iterative version uses a loop
to multiply numbers from 2 to `n`, eliminating the need for multiple function calls, which saves
memory and avoids stack overflow issues.

Recursive Implementation:

#include <stdio.h>

int factorial_recursive(int n) {

if (n == 0 || n == 1) // Base case

return 1;

return n * factorial_recursive(n - 1); // Recursive case

int main() {

int num = 5; // Change the number for different results

printf("Factorial of %d (recursive): %d\n", num, factorial_recursive(num));

return 0;

Iterative Implementation:

#include <stdio.h>

int factorial_iterative(int n) {

int result = 1;

for (int i = 2; i <= n; i++) // Iterating from 2 to n

result *= i;

return result;

int main() {

int num = 5; // Change the number for different results

printf("Factorial of %d (iterative): %d\n", num, factorial_iterative(num));

return 0;

This iterative version avoids recursion and uses a loop to compute the factorial.
Ans 2- Towers of Hanoi Problem (C Language)

The Towers of Hanoi involves moving disks between three rods, following specific rules: only one disk
can be moved at a time, and no larger disk may be placed on a smaller one. The recursive solution is
to move n-1 disks to an auxiliary rod, then move the largest disk, followed by moving the n-1 disks to
the destination.

Example

#include <stdio.h>

void move_disks(int n, char source, char destination, char auxiliary) {

if (n == 1) {

printf("Move disk 1 from %c to %c\n", source, destination);

return;

move_disks(n - 1, source, auxiliary, destination); // Step 1

printf("Move disk %d from %c to %c\n", n, source, destination); // Step 2

move_disks(n - 1, auxiliary, destination, source); // Step 3

int main() {

int num_disks = 3; // Change the number of disks here

move_disks(num_disks, 'A', 'C', 'B'); // A, C, B are the rod names

return 0;

Output

For 3 disks, the output will be:

Move disk 1 from A to C

Move disk 2 from A to B

Move disk 1 from C to B

Move disk 3 from A to C

Move disk 1 from B to A

Move disk 2 from B to C

Move disk 1 from A to C


Ans 3-

Trade-offs Between Iteration and Recursion

1. Memory Usage:

o Recursion: Each call adds to the call stack, potentially leading to stack overflow.

o Iteration: Uses a fixed amount of memory, making it more efficient.

2. Performance:

o Recursion: Overhead from multiple function calls can slow execution.

o Iteration: Generally faster, avoiding function call overhead.

3. Readability:

o Recursion: More concise for problems like tree traversals.

o Iteration: Can be less intuitive for complex tasks but clearer for simple loops.

4. Usability:

o Recursion: Ideal for problems that can be divided into subproblems.

o Iteration: Best for straightforward tasks like counting or summing.

Example

Fibonacci Sequence:

• Recursive:

int fib(int n) { return n <= 1 ? n : fib(n-1) + fib(n-2); } // Exponential time

• Iterative:

int fib(int n) { int a = 0, b = 1; for (int i = 2; i <= n; i++) { int temp = a; a = b; b = temp + b; }
return b; } // Linear time

Ans 4-

Circular Queue

A circular queue is a data structure that uses a fixed-size array in a circular fashion. It allows for
efficient use of space by connecting the end of the queue back to the beginning. When the end of
the queue is reached, new elements can be added at the front if there is available space, thus
avoiding wastage of space that occurs in a linear queue.

Differences from Linear Queue

1. Structure:

o Linear Queue: In a linear queue, elements are added at the rear and removed from
the front, leading to potential wastage of space when elements are dequeued.
o Circular Queue: The last position is connected to the first position, making it circular.
This allows for more efficient use of space.

2. Overflow Condition:

o Linear Queue: Overflow occurs when the rear reaches the end of the array.

o Circular Queue: Overflow occurs when the next position of the rear equals the front.

3. Efficiency:

o Circular queues make better use of memory and can avoid the need for shifting
elements when dequeueing.

C Program for Circular Queue Implementation

#include <stdio.h>

#include <stdlib.h>

#define MAX 5

typedef struct {

int items[MAX];

int front, rear;

} CircularQueue;

void initQueue(CircularQueue* q) {

q->front = -1;

q->rear = -1;

int isFull(CircularQueue* q) {

return (q->rear + 1) % MAX == q->front;

int isEmpty(CircularQueue* q) {

return q->front == -1;

}
void enqueue(CircularQueue* q, int value) {

if (isFull(q)) {

printf("Queue is full!\n");

return;

if (isEmpty(q)) {

q->front = 0; // First element

q->rear = (q->rear + 1) % MAX;

q->items[q->rear] = value;

printf("Enqueued: %d\n", value);

int dequeue(CircularQueue* q) {

if (isEmpty(q)) {

printf("Queue is empty!\n");

return -1;

int value = q->items[q->front];

if (q->front == q->rear) {

q->front = q->rear = -1; // Reset queue

} else {

q->front = (q->front + 1) % MAX;

return value;

void display(CircularQueue* q) {

if (isEmpty(q)) {

printf("Queue is empty!\n");
return;

printf("Queue: ");

int i = q->front;

while (1) {

printf("%d ", q->items[i]);

if (i == q->rear) break;

i = (i + 1) % MAX;

printf("\n");

int main() {

CircularQueue q;

initQueue(&q);

enqueue(&q, 10);

enqueue(&q, 20);

enqueue(&q, 30);

display(&q);

printf("Dequeued: %d\n", dequeue(&q));

display(&q);

enqueue(&q, 40);

enqueue(&q, 50);

enqueue(&q, 60);

display(&q);

return 0;

}
This C program implements a circular queue with functions for initializing the queue, checking if it's
full or empty, enqueuing, dequeuing, and displaying its contents. The circular structure helps utilize
space efficiently, making it advantageous over a linear queue.

Ans 5-

Dequeue (Double-ended Queue)

A dequeue (or double-ended queue) is a linear data structure that allows insertion and deletion of
elements from both ends—front and rear. It can be considered as a combination of both stack and
queue.

Operations on Dequeue

1. Insertion:

o Insert Front: Adds an element at the front of the dequeue.

o Insert Rear: Adds an element at the rear of the dequeue.

2. Deletion:

o Delete Front: Removes an element from the front of the dequeue.

o Delete Rear: Removes an element from the rear of the dequeue.

3. Other Operations:

o isEmpty: Checks if the dequeue is empty.

o isFull: Checks if the dequeue is full (if using a fixed size).

o Get Front: Retrieves the front element without removing it.

o Get Rear: Retrieves the rear element without removing it.

Example

Consider a scenario where a dequeue is used to manage tasks in a scheduler. You can add tasks to
the front or back based on priority or other criteria.
C Implementation of Dequeue

#include <stdio.h>

#include <stdlib.h>

#define MAX 5

typedef struct {

int items[MAX];

int front, rear;

} Dequeue;

void initDequeue(Dequeue* dq) {

dq->front = -1;

dq->rear = -1;

int isFull(Dequeue* dq) {

return (dq->front == (dq->rear + 1) % MAX);

int isEmpty(Dequeue* dq) {

return (dq->front == -1);

void insertFront(Dequeue* dq, int value) {

if (isFull(dq)) {

printf("Dequeue is full!\n");

return;

if (isEmpty(dq)) {
dq->front = dq->rear = 0; // First element

} else {

dq->front = (dq->front - 1 + MAX) % MAX; // Circular decrement

dq->items[dq->front] = value;

printf("Inserted %d at front\n", value);

void insertRear(Dequeue* dq, int value) {

if (isFull(dq)) {

printf("Dequeue is full!\n");

return;

if (isEmpty(dq)) {

dq->front = dq->rear = 0; // First element

} else {

dq->rear = (dq->rear + 1) % MAX; // Circular increment

dq->items[dq->rear] = value;

printf("Inserted %d at rear\n", value);

int deleteFront(Dequeue* dq) {

if (isEmpty(dq)) {

printf("Dequeue is empty!\n");

return -1;

int value = dq->items[dq->front];

if (dq->front == dq->rear) {

dq->front = dq->rear = -1; // Reset when empty

} else {
dq->front = (dq->front + 1) % MAX; // Circular increment

return value;

int deleteRear(Dequeue* dq) {

if (isEmpty(dq)) {

printf("Dequeue is empty!\n");

return -1;

int value = dq->items[dq->rear];

if (dq->front == dq->rear) {

dq->front = dq->rear = -1; // Reset when empty

} else {

dq->rear = (dq->rear - 1 + MAX) % MAX; // Circular decrement

return value;

void display(Dequeue* dq) {

if (isEmpty(dq)) {

printf("Dequeue is empty!\n");

return;

printf("Dequeue: ");

for (int i = dq->front; i != dq->rear; i = (i + 1) % MAX) {

printf("%d ", dq->items[i]);

printf("%d\n", dq->items[dq->rear]); // Print last element

}
int main() {

Dequeue dq;

initDequeue(&dq);

insertRear(&dq, 10);

insertRear(&dq, 20);

insertFront(&dq, 30);

display(&dq);

printf("Deleted from front: %d\n", deleteFront(&dq));

display(&dq);

printf("Deleted from rear: %d\n", deleteRear(&dq));

display(&dq);

return 0;

This C program implements a dequeue with functions to insert and delete elements from both ends.
The circular structure allows for efficient space utilization, making the dequeue a versatile data
structure for various applications.

Ans 6 –

Priority Queue

A priority queue is an abstract data type similar to a regular queue but with an added feature: each
element has a priority assigned to it. Elements are served based on their priority rather than their
order in the queue. In a priority queue, higher priority elements are dequeued before lower priority
ones, regardless of their position in the queue.
Differences from Regular Queue

1. Ordering:

Regular Queue: Elements are processed in the order they are added (FIFO - First In, First Out).

Priority Queue: Elements are processed based on their priority. A higher priority element can be
served before lower priority elements, even if it was added later.

2. Implementation:

Regular Queue: Can be implemented using arrays or linked lists with straightforward enqueue and
dequeue operations.

Priority Queue: Often implemented using heaps (binary heaps), which allow for efficient priority-
based operations.

3. Operations:

Regular Queue: Basic operations are enqueue (add) and dequeue (remove).

Priority Queue: Operations include enqueue (with priority) and dequeue (removing the highest
priority element).

Real-life Examples of Priority Queues

1. Job Scheduling in Operating Systems:

Tasks with higher priority (e.g., system processes) are executed before lower-priority tasks (e.g.,
background processes).

2. Emergency Room Triage:

Patients are treated based on the severity of their condition rather than their arrival time. Critical
patients are prioritized over less severe cases.

3. Print Spooling:

Print jobs are prioritized based on the document type or size. Important documents (e.g., legal
papers) might be printed before regular documents.

4. Event Management:
In event-driven systems, high-priority events (like interrupts in hardware systems) are processed
before lower-priority events.

Priority queues are essential in scenarios where specific tasks need to be prioritized over others,
enhancing efficiency in processing and resource allocation in various applications.

You might also like