Solved_Unit_2_Q-Bank
Solved_Unit_2_Q-Bank
1. Push Operation
Theory:
The push operation adds a new element to the top of the stack. Before adding, it checks if
the stack is full (to prevent overflow). If not, it increments the top pointer and inserts the
element.
Function Code:
2. Pop Operation
Theory:
The pop operation removes the top element from the stack. Before removing, it checks if
the stack is empty (to avoid underflow). If not, it returns the top element and decrements the top
pointer.
Function Code:
int pop(Stack* stack) {
if (stack->top == -1) {
printf("Stack Underflow! Cannot pop.\n");
return -1;
}
return stack->arr[stack->top--];
}
3. Peek Operation
Theory:
The peek operation retrieves the top element of the stack without removing it. It checks if
the stack is empty. If it’s not empty, it returns the value at the top pointer.
Function Code:
int peek(Stack* stack) {
if (stack->top == -1) {
printf("Stack is empty! Cannot peek.\n");
return -1;
}
return stack->arr[stack->top];
}
4. IsEmpty Operation
Theory:
The isEmpty operation checks if the stack is empty by verifying if the top pointer equals
-1. It is often used before other operations like pop or peek to prevent underflow errors.
Function Code:
bool isEmpty(Stack* stack) {
return stack->top == -1;
}
5. IsFull Operation
Theory:
The isFull operation checks if the stack has reached its maximum capacity. It is
typically used before push to avoid overflow.
Function Code:
bool isFull(Stack* stack) {
return stack->top == MAX - 1;
}
Combine Example –
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct {
int arr[MAX];
int top;
} Stack;
void initialize(Stack* stack) {
stack->top = -1;
}
int main() {
Stack stack;
initialize(&stack);
push(&stack, 10);
push(&stack, 20);
push(&stack, 30);
printf("Top element is: %d\n", peek(&stack));
return 0;
}
Code Output -
2. Dequeue Operation
Theory:
The dequeue operation removes and returns the element at the front of the queue. It
checks if the queue is empty before removing. In a circular queue, the front pointer wraps around
when it reaches the maximum capacity.
Function Code:
int dequeue(int queue[], int* rear, int* front, int size) {
if (*front == -1) {
printf("Queue Underflow! Cannot dequeue.\n");
return -1;
}
int value = queue[*front];
if (*front == *rear) {
*front = -1;
*rear = -1;
} else {
*front = (*front + 1) % size;
}
return value;
}
Function Code:
int peek(int queue[], int* front) {
if (*front == -1) {
printf("Queue is empty! Cannot peek.\n");
return -1;
}
return queue[*front];
}
4. IsEmpty Operation
Theory:
The isEmpty operation checks if the queue has no elements by verifying if the front
pointer is -1.
Function Code:
bool isEmpty(int* front) {
return *front == -1;
}
5. IsFull Operation
Theory:
The isFull operation checks if the queue is full by comparing the rear pointer and front
pointer in a circular manner.
Function Code:
bool isFull(int* rear, int* front, int size) {
return (*rear + 1) % size == *front;
}
Combine Example –
#include <stdio.h>
#include <stdbool.h>
#define SIZE 5
typedef struct {
int arr[SIZE];
int front;
int rear;
} Queue;
void initialize(Queue* queue) {
queue->front = -1;
queue->rear = -1;
}
bool isEmpty(Queue* queue) {
return queue->front == -1;
}
bool isFull(Queue* queue) {
return (queue->rear + 1) % SIZE == queue->front;
}
void enqueue(Queue* queue, int value) {
if (isFull(queue)) {
printf("Queue Overflow! Cannot enqueue %d.\n", value);
return;
}
if (isEmpty(queue)) queue->front = 0;
queue->rear = (queue->rear + 1) % SIZE;
queue->arr[queue->rear] = value;
printf("%d enqueued to the queue.\n", value);
}
int dequeue(Queue* queue) {
if (isEmpty(queue)) {
printf("Queue Underflow! Cannot dequeue.\n");
return -1;
}
int value = queue->arr[queue->front];
if (queue->front == queue->rear) {
queue->front = -1;
queue->rear = -1;
} else {
queue->front = (queue->front + 1) % SIZE;
}
return value;
}
int peek(Queue* queue) {
if (isEmpty(queue)) {
printf("Queue is empty! Cannot peek.\n");
return -1;
}
return queue->arr[queue->front];
}
int main() {
Queue queue;
initialize(&queue);
enqueue(&queue, 10);
enqueue(&queue, 20);
enqueue(&queue, 30);
enqueue(&queue, 40);
enqueue(&queue, 50);
printf("Front element is: %d\n", peek(&queue));
printf("Dequeued element: %d\n", dequeue(&queue));
printf("Dequeued element: %d\n", dequeue(&queue));
enqueue(&queue, 60);
printf("Front element is: %d\n", peek(&queue));
while (!isEmpty(&queue)) {
printf("Dequeued element: %d\n", dequeue(&queue));
}
dequeue(&queue);
return 0;
}
Code Output –
Applications of Stack:
Stacks are widely used in various applications because of their LIFO (Last In, First Out) nature.
Here are some of the key applications:
1. Function Calls (Recursion):
• Description:
Stacks are essential in managing function calls in a program. When a function is
called, its state (such as local variables, return address, etc.) is saved onto the
stack. When the function execution is complete, the state is popped from the
stack, and the control returns to the calling function. This is known as the call
stack.
• Example:
Recursive function calls use the stack to maintain the state and return values at
each level.
2. Web Browser History:
• Description:
Web browsers use stacks to store the history of pages visited. When the user
clicks the back button, the browser pops the most recent URL from the stack and
navigates back to that page.
• Example:
The "back" and "forward" buttons in web browsers use a stack to manage
navigation history.
3. Memory Management:
• Description:
Stacks play a role in memory management in languages like C and C++. The call
stack stores the function calls and local variables. When a function returns, the
memory allocated for local variables is released automatically.
• Example:
Stack frames are created when functions are called, and they are destroyed when
functions return.
typedef struct {
int arr[SIZE];
int front;
int rear;
} Queue;
int main() {
Queue queue;
initialize(&queue);
enqueue(&queue, 10);
enqueue(&queue, 20);
enqueue(&queue, 30);
enqueue(&queue, 40);
enqueue(&queue, 50);
enqueue(&queue, 60);
return 0;
}
Program Output –
Q6 - What are the postfix and prefix forms of the expression? A+B*(C-D)/(P-R)
Ans – ( IF YOU GOT THE ANS THEN SEND ME TOO )
Expression Result
Infix A+B×(C−D)/(P−R)
Postfix ABCD−∗PR−/+
Prefix +A/∗B−CD−PR
1. Direct Recursion
Definition:
In direct recursion, a function calls itself directly within its body to solve a smaller instance of
the same problem.
• Characteristics:
o The function explicitly invokes itself.
o A base condition is used to terminate the recursion.
• Example:
Calculating the factorial of a number.
int factorial(int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
2. Indirect Recursion
Definition:
In indirect recursion, a function calls another function, which in turn calls the original function,
creating a loop of function calls.
• Characteristics:
o Requires at least two functions.
o The sequence of function calls forms a cycle.
• Example:
Two functions indirectly calling each other.
void functionA(int n) {
if (n > 0) {
printf("%d ", n);
functionB(n - 1);
}
}
void functionB(int n) {
if (n > 0) {
printf("%d ", n);
functionA(n / 2);
}
}
3. Tail Recursion
Definition:
In tail recursion, the recursive call is the last operation performed by the function before it
returns.
• Characteristics:
o Optimized by compilers to reduce memory usage (tail call optimization).
o Eliminates the need for maintaining a call stack for intermediate computations.
• Example:
Calculating the factorial of a number using tail recursion.
int factorialTail(int n, int result) {
if (n == 0) return result;
return factorialTail(n - 1, result * n);
}
4. Non-Tail Recursion
Definition:
In non-tail recursion, the recursive call is not the last operation in the function. Some operations
remain to be performed after the recursive call.
• Characteristics:
o Requires the call stack to keep track of intermediate states.
o Not optimized by compilers for tail call optimization.
• Example:
Calculating the Fibonacci sequence.
int fibonacci(int n) {
if (n == 0) return 0; // Base case
if (n == 1) return 1; // Base case
return fibonacci(n - 1) + fibonacci(n - 2); // Recursive call, followed by
addition
}
Q8 - Evaluation of a postfix expression using a stack 653+9*+
Ans –
Step-by-Step Evaluation Table
Push Operation
The Push operation inserts an element onto the top of the stack.
Algorithm:
➢ Where :
• The stack S (a container that holds elements).
• An integer x to be pushed onto the stack.
➢ Steps:
• Check for Stack Overflow: If top == MAX - 1 (stack is full), return an Overflow
error.
• Push the element: Increase the top by 1 (to move the index to the next available
position).
• Assign the value of x to S[top] (add the element at the new position).
• End.
Pop Operation
Algorithm:
➢ Where:
• The stack S (a container holding elements).
• The Pointer of Stack top
➢ Steps:
• Check for Stack Underflow: If top == -1 (stack is empty), return an Underflow
error.
• Pop the element: Retrieve the value at S[top] (the element to be removed).
• Store this value in a variable, element.
• Decrease the top by 1 (to move the index down and remove the element).
• Return the value element (the popped element).
• End.
Applications:
• Multitasking Operating Systems: Round Robin is widely used for process scheduling in
general-purpose operating systems like Linux, Windows, and macOS.
• Time-sharing Systems: It is effective for systems where multiple users are interacting
with the computer, and each user’s process needs a fair share of the CPU time.
Q12 - Define Queue, Circular Queue, Dequeue, Priority Queue?
Ans –
Queue
A queue is a linear data structure that follows the First In, First Out (FIFO) principle,
meaning the element that is inserted first will be the first one to be removed. It is similar to a
queue at a checkout counter, where customers are served in the order they arrive.
Basic Operations of a Queue:
• Enqueue: Add an element to the back of the queue.
• Dequeue: Remove an element from the front of the queue.
• Peek/Front: View the front element without removing it.
• IsEmpty: Check if the queue is empty.
• IsFull: Check if the queue is full (in case of a fixed-size queue).
Circular Queue
A circular queue is a type of queue in which the last position is connected back to the
first position, forming a circle. This overcomes the problem of wasted space in a regular queue
when elements are dequeued from the front but new elements are still added to the rear.
Key Characteristics:
• The queue is circular, meaning after reaching the last element, the next element is added
to the front if there is space.
• The front and rear pointers are used to track the positions of the elements in the queue.
• A circular queue makes better use of available space in a fixed-size queue.
Basic Operations:
• Enqueue: Add an element to the rear, wrapping around if necessary.
• Dequeue: Remove an element from the front, adjusting the pointers appropriately.
• IsFull: Check if the queue has no more space to add an element.
• IsEmpty: Check if the queue is empty (i.e., when the front equals the rear).
Q13 - What are enqueue and dequeue operations? Write the code for it?
Ans –
Enqueue and Dequeue Operations in Queue
In a queue, the enqueue and dequeue operations are used to add and remove elements,
respectively. A queue follows the FIFO (First In, First Out) principle, meaning the first
element added to the queue will be the first one to be removed.
Enqueue Operation
The enqueue operation is used to add an element to the rear (or back) of the queue.
The enqueue function checks if the queue is full by comparing rear to MAX - 1. If the
queue is not full, the element is added to the rear, and the rear pointer is incremented. If
the queue was empty, the front pointer is also set to 0.
Operation Code :
void enqueue(Queue* q, int value) {
if (q->rear == MAX - 1) {
printf("Queue Overflow! Cannot enqueue %d\n", value);
return;
}
if (q->front == -1) {
q->front = 0;
}
q->rear++;
q->arr[q->rear] = value;
printf("%d enqueued to the queue.\n", value);
}
Dequeue Operation
The dequeue operation removes and returns the element at the front of the queue.
The dequeue function checks if the queue is empty by checking if front = -1. If the
queue is not empty, it returns the element at the front and moves the front pointer
forward. If the queue becomes empty after the dequeue, both front and rear are reset to -
1.
Operation Code :
int dequeue(Queue* q) {
if (q->front == -1) {
printf("Queue Underflow! Cannot dequeue.\n");
return -1;
}
int dequeuedElement = q->arr[q->front];
if (q->front == q->rear) {
q->front = q->rear = -1;
} else {
q->front++;
}
return dequeuedElement;
}
Q14 - A circular queue has a size of 5 and has 3 elements 10,20 and 40 where F=2 and
R=4. After inserting 50 and 60, what is the value of F and R. Trying to insert 30 at this
stage what happens? Delete 2 elements from the queue and insert 70, 80 & 90. Show
the sequence of steps with necessary diagrams with the value of F & R.
Ans – ( Diagram Representation According to Conditions. Below Ans Is Not For Test Paper It
Just For Understanding ).
Initial State:
• Size of the circular queue = 5
• Elements in the queue = [10, 20, 40]
• Front (F) = 2
• Rear (R) = 4
The queue appears as follows:
Index 0 1 2 3 4
Value - - 10 20 40
Status Empty Empty Front Rear
Summary:
1. After inserting 50 and 60:
o Front (F) = 2, Rear (R) = 1.
2. Trying to insert 30:
o Queue Overflow, as the queue is full.
3. After deleting 2 elements:
o Front (F) = 4, Rear (R) = 1.
4. After inserting 70, 80, and 90:
o Front (F) = 4, Rear (R) = 3.
This step-by-step explanation illustrates the state of the queue, handling both overflow and
normal operations.
• The isEmpty function checks whether the front pointer is set to -1, which indicates
that the queue is empty.
• When the queue is initialized, both front and rear are set to -1, so the queue is
empty.
• After all elements are dequeued, the front is reset to -1 to indicate an empty queue.
Example of Usage in Context
#include <stdio.h>
#include <stdlib.h>
#define MAX 5
typedef struct {
int arr[MAX];
int front;
int rear;
} Queue;
int isEmpty(Queue* q) {
return (q->front == -1);
}
int main() {
Queue q = {{0}, -1, -1}; // Initialize an empty queue
if (isEmpty(&q)) {
printf("Queue is empty.\n");
} else {
printf("Queue is not empty.\n");
}
return 0;
}
Ans –
int main() {
Stack s = {{0}, -1}; // Initialize stack
pop(&s); // Underflow example
push(&s, 10);
push(&s, 20);
push(&s, 30);
push(&s, 40);
push(&s, 50);
push(&s, 60); // Overflow example
return 0;
}
Stack Diagram:
Queue Code:
#include <stdio.h>
#define MAX 5
typedef struct {
int arr[MAX];
int front, rear;
} Queue;
int isFull(Queue* q) {
return (q->rear + 1) % MAX == q->front;
}
int isEmpty(Queue* q) {
return q->front == -1;
}
void enqueue(Queue* q, int value) {
if (isFull(q)) {
printf("Queue Overflow! Cannot enqueue %d.\n", value);
return;
}
if (q->front == -1) {
q->front = 0;
}
q->rear = (q->rear + 1) % MAX;
q->arr[q->rear] = value;
printf("%d enqueued.\n", value);
}
int dequeue(Queue* q) {
if (isEmpty(q)) {
printf("Queue Underflow! Cannot dequeue.\n");
return -1;
}
int dequeued = q->arr[q->front];
if (q->front == q->rear) {
q->front = q->rear = -1;
} else {
q->front = (q->front + 1) % MAX;
}
return dequeued;
}
int main() {
Queue q = {{0}, -1, -1};
dequeue(&q); // Underflow example
enqueue(&q, 10);
enqueue(&q, 20);
enqueue(&q, 30);
enqueue(&q, 40);
enqueue(&q, 50);
enqueue(&q, 60); // Overflow example
return 0;
}
Queue Diagram:
Index 0 1 2 3 4
Value - - - - -
Front -1
Rear -1
2. After Enqueue (10, 20, 30, 40, 50):
Index 0 1 2 3 4
Value 10 20 30 40 50
Front 0
Rear 4