0% found this document useful (0 votes)
15 views

dsa

Uploaded by

drrishi1990
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views

dsa

Uploaded by

drrishi1990
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 14

Q.1 Give the algorithm of Tower of Hanoi problem with n disks.

Derive the total number of moves in


this problem.

The Tower of Hanoi problem involves moving nnn disks from a source peg (A) to a destination peg (C)
using an auxiliary peg (B), following these rules Only one disk can be moved at a time.
1. A disk can only be placed on an empty peg or on top of a larger disk.
2. No disk may be placed on top of a smaller disk.
The recursive algorithm for solving the Tower of Hanoi problem is as follows:
1. Base Case: If there is only one disk, move it directly from the source peg to the destination peg.
2. Recursive Case:
o Move the top n−1n-1n−1 disks from the source peg (A) to the auxiliary peg (B).
o Move the nthn^{th}nth (largest) disk from the source peg (A) to the destination peg (C).
o Move the n−1n-1n−1 disks from the auxiliary peg (B) to the destination peg
(C).Recursive Formula:
Let T(n)T(n)T(n) represent the minimum number of moves needed to solve the Tower of Hanoi problem
with nnn disks. The recursive relation is:
T(n)=2T(n−1)+1T(n) = 2T(n-1) + 1T(n)=2T(n−1)+1
Where:
 2T(n−1)2T(n-1)2T(n−1) accounts for moving n−1n-1n−1 disks twice, and
 +1+1+1 accounts for moving the largest disk.
Base Case:
For n=1n = 1n=1, we need exactly 1 move:
T(1)=1T(1) = 1T(1)=1
Deriving the Total Number of Moves:
By expanding the recurrence, we get:

T(n−2)=2T(n−3)+1T(n-2) = 2T(n-3) + 1T(n−2)=2T(n−3)+1 ⋮\vdots⋮


T(n)=2T(n−1)+1T(n) = 2T(n-1) + 1T(n)=2T(n−1)+1 T(n−1)=2T(n−2)+1T(n-1) = 2T(n-2) + 1T(n−1)=2T(n−2)+1

After expanding all terms, we get the sum:


T(n)=2n−1T(n) = 2^n - 1T(n)=2n−1
Thus, the total number of moves required to solve the Tower of Hanoi problem with nnn disks is
T(n)=2n−1T(n) = 2^n - 1T(n)=2n−1.
Example:
 For n=1n = 1n=1, T(1)=21−1=1T(1) = 2^1 - 1 = 1T(1)=21−1=1 move.
 For n=3n = 3n=3, T(3)=23−1=7T(3) = 2^3 - 1 = 7T(3)=23−1=7 moves.
 For n=4n = 4n=4, T(4)=24−1=15T(4) = 2^4 - 1 = 15T(4)=24−1=15 moves.
Conclusion:
The Tower of Hanoi problem requires 2n−12^n - 12n−1 moves to solve with nnn disks. This exponential
growth in the number of moves makes the problem more complex as the number of disks increases.
Q.2 The keys 12, 18, 13,2, 3,23, 5 and 15 are inserted into an initially empty hash table of What i
eroguan adrea it how fineriodit told
It seems like your question got a bit garbled, but from what I understand, you're asking about inserting a
series of keys into an initially empty hash table and probably want to know about the process, hash
function, and handling collisions.
Here's a medium-level answer based on the assumption that you're working with a hash table and some
method for collision handling (such as linear probing or separate chaining):
Keys to be Inserted:
The keys are: 12, 18, 13, 2, 3, 23, 5, 15
Hash Table Size:
Let’s assume the size of the hash table is 10 (for simplicity). This can vary depending on the specific
problem.
Hash Function:
We’ll use a simple hash function for demonstration purposes. A common hash function is:
h(k)=kmod table sizeh(k) = k \mod \text{table size}h(k)=kmodtable size
So, the hash function will compute the remainder when each key is divided by 10.
Step-by-Step Insertions:
1. Insert 12:
h(12)=12mod 10=2h(12) = 12 \mod 10 = 2h(12)=12mod10=2
The key 12 is inserted at index 2 in the hash table.
2. Insert 18:
h(18)=18mod 10=8h(18) = 18 \mod 10 = 8h(18)=18mod10=8
The key 18 is inserted at index 8.
3. Insert 13:
h(13)=13mod 10=3h(13) = 13 \mod 10 = 3h(13)=13mod10=3
The key 13 is inserted at index 3.
4. Insert 2:
h(2)=2mod 10=2h(2) = 2 \mod 10 = 2h(2)=2mod10=2
Index 2 is already occupied by 12, so we need to resolve the collision. Let’s use linear probing (if a slot is
occupied, move to the next slot). The next available index is 4. The key 2 is inserted at index 4.
5. Insert 3:
h(3)=3mod 10=3h(3) = 3 \mod 10 = 3h(3)=3mod10=3
Index 3 is already occupied by 13, so we use linear probing and check index 4, which is also occupied by
2. The next available slot is index 5. The key 3 is inserted at index 5.
6. Insert 23:
h(23)=23mod 10=3h(23) = 23 \mod 10 = 3h(23)=23mod10=3
Index 3 is occupied (by 13), index 4 is occupied (by 2), and index 5 is occupied (by 3). We continue
probing and find the next available slot at index 6. The key 23 is inserted at index 6.
7. Insert 5:
h(5)=5mod 10=5h(5) = 5 \mod 10 = 5h(5)=5mod10=5
Index 5 is already occupied by 3, so we use linear probing and find index 7 to be available. The key 5 is
inserted at index 7.
8. Insert 15:
h(15)=15mod 10=5h(15) = 15 \mod 10 = 5h(15)=15mod10=5
Index 5 is occupied (by 3), index 6 is occupied (by 23), and index 7 is occupied (by 5). We continue
probing and find that index 9 is available. The key 15 is inserted at index 9.
Final Hash Table:
After inserting all the keys, the hash table looks like this (with keys and their respective indices):
Index Key
0
1
2 12
3 13
4 2
5 3
6 23
7 5
8 18
9 15
Conclusion:
By applying the hash function and resolving collisions using linear probing, we successfully inserted all
the keys into the hash table. Each key is placed at the index determined by the hash function, and if a
collision occurs, linear probing is used to find the next available slot.

Q3, Write algorithms to insert and delete an element from array implementation of circular queue.
Circular Queue: Insertion and Deletion Algorithms
A Circular Queue is a type of queue in which the positions of the elements wrap around in a circular
fashion. It uses a fixed-size array to store elements, and when the queue reaches the end, it continues
from the beginning of the array.
In a circular queue, there are two pointers:
1. Front: Points to the front of the queue (where elements are dequeued).
2. Rear: Points to the last element of the queue (where elements are enqueued).
The key feature of a circular queue is that it allows the reuse of unused spaces when elements are
dequeued.
Insertion Algorithm (Enqueue)
The insert (enqueue) operation adds an element at the rear of the circular queue.
1. Check if the queue is full:
o A queue is full if the next position of the rear is the front (i.e., (rear + 1) % size == front).
2. Update the rear pointer:
o If space is available, increment the rear pointer and insert the element.
o Update the queue array at the new rear position.
Algorithm for Insertion (Enqueue):
python
Copy code
def enqueue(queue, front, rear, size, element):
# Check if the queue is full
if (rear + 1) % size == front:
print("Queue is full")
else:
# If queue is not full, insert the element
if front == -1: # if the queue is initially empty
front = 0
rear = (rear + 1) % size # Update rear in a circular manner
queue[rear] = element # Insert element at the rear
print(f"Inserted {element} at position {rear}")
return front, rear
Deletion Algorithm (Dequeue)
The delete (dequeue) operation removes an element from the front of the circular queue.
1. Check if the queue is empty:
o The queue is empty if the front is equal to -1.
2. Update the front pointer:
o If the queue is not empty, increment the front pointer.
o If the front reaches the rear, set both front and rear to -1, indicating the queue is empty.
Algorithm for Deletion (Dequeue):
python
Copy code
def dequeue(queue, front, rear, size):
# Check if the queue is empty
if front == -1:
print("Queue is empty")
else:
removed_element = queue[front] # Remove element at front
print(f"Removed {removed_element} from position {front}")

if front == rear: # Only one element was in the queue


front = -1 # Queue is now empty
rear = -1
else:
front = (front + 1) % size # Move front in a circular manner
return front, rear
Explanation of the Algorithms:
1. Enqueue (Insertion):
o If the queue is full, the algorithm prints a message indicating that no element can be
inserted.
o If the queue is not full, the rear pointer is updated in a circular manner using the
modulus operator.
o If the queue is empty (i.e., front is -1), the front pointer is set to 0.
2. Dequeue (Deletion):
o If the queue is empty, the algorithm prints a message indicating that no element can be
removed.
o If the queue is not empty, the front pointer is updated circularly.
o If only one element remains, both the front and rear are reset to -1, indicating that the
queue is now empty.
Example:
Let’s assume the queue has a size of 5 and is initially empty.
Step-by-Step Example:
1. Initial Queue: [None, None, None, None, None]
o front = -1, rear = -1
2. Enqueue 10:
o front = 0, rear = 0
o Queue after insertion: [10, None, None, None, None]
3. Enqueue 20:
o front = 0, rear = 1
o Queue after insertion: [10, 20, None, None, None]
4. Dequeue (Remove 10):
o front = 1, rear = 1
o Queue after deletion: [10, 20, None, None, None]
5. Enqueue 30:
o front = 1, rear = 2
o Queue after insertion: [10, 20, 30, None, None]
Final Queue:
 After multiple insertions and deletions, the queue would update accordingly based on the above
algorithms.
Time Complexity:
 Enqueue: O(1)O(1)O(1) (constant time).
 Dequeue: O(1)O(1)O(1) (constant time).
Conclusion:
The circular queue is an efficient data structure that allows continuous use of space without needing to
shift elements after deletions. The insertion and deletion operations are simple and efficient, each taking
constant time, O(1)O(1)O(1).

Q4, Illustrate execution of quick-sort (in increasing order) in the sequence


44 33 11 55 77 90 40 60 99 22 88
QuickSort Algorithm - Execution
QuickSort is a divide-and-conquer algorithm. It selects a pivot element, partitions the array such that
elements smaller than the pivot are on the left and elements greater than the pivot are on the right, and
then recursively sorts the sub-arrays.
Here’s how QuickSort works for the given array in increasing order:
Array: 44, 33, 11, 55, 77, 90, 40, 60, 99, 22, 88
Step-by-Step Execution:
1. Initial Array:
44, 33, 11, 55, 77, 90, 40, 60, 99, 22, 88
2. First Call (Pivot = 44):
o Partitioning Step: Select 44 as the pivot. We arrange elements such that all values less
than 44 go to the left and all values greater than 44 go to the right.
o After partitioning, the array becomes:
33, 11, 40, 22, 44, 77, 90, 55, 99, 60, 88
(44 is now placed at its correct position)
Now we recursively sort the left and right subarrays.
Left Subarray (Elements less than 44):
Subarray: 33, 11, 40, 22
 Pivot = 33
 Partitioning Step: Rearrange to place elements less than 33 on the left and greater on the right.
Array becomes: 11, 22, 33, 40
 Now 33 is at its correct position, and we sort the subarrays 11, 22 and 40.
Sorting 11, 22:
 Pivot = 11
Since there’s only one element on the left (11), it's already sorted. The array remains:
11, 22
Sorting 40:
 Pivot = 40
Since there is only one element, no changes are made.
Now, the left subarray is sorted: 11, 22, 33, 40
Right Subarray (Elements greater than 44):
Subarray: 77, 90, 40, 60, 99, 22, 88
 Pivot = 77
Rearrange to place elements less than 77 on the left and greater on the right.
Array becomes: 60, 40, 55, 77, 99, 88, 90
Now 77 is at its correct position, and we sort the subarrays 60, 40, 55 and 99, 88, 90.
Sorting the Left Subarray 60, 40, 55:
 Pivot = 60
Rearranged array: 40, 55, 60
So, 60 is placed at its correct position, and we have:
o 40 and 55 subarrays.
o Sorted 40 and 55 give 40, 55.
Sorting the Right Subarray 99, 88, 90:
 Pivot = 90
Rearrange to place smaller elements on the left and larger on the right.
Sorted array becomes: 88, 90, 99
Final Array after all Sorting Steps:
The array after all sorting steps is:
Copy code
11, 22, 33, 40, 44, 55, 60, 77, 88, 90, 99
Conclusion:
QuickSort successfully sorted the array in increasing order with the following key operations:
1. Partitioning the array around a pivot.
2. Recursively sorting subarrays on the left and right of the pivot.
3. Final sorted array: 11, 22, 33, 40, 44, 55, 60, 77, 88, 90, 99
Q5, Find the time complexity of the following code and mention it in Big O
int fun(int n)

int count = 0;
for (int j=0:jajtt)
for (int k= 0; k <j; k++) count+= 1;
return count;
Time Complexity Analysis of the Given Code
Let's break down the given code step by step and analyze its time complexity.
cpp
Copy code
int fun(int n) {
int count = 0;
for (int j = 0; j < n; j++) // Outer loop
for (int k = 0; k < j; k++) // Inner loop
count += 1;
return count;
}
Step-by-Step Execution:
1. Outer Loop (variable j):
o The outer loop runs from j = 0 to j = n - 1, so it iterates n times.
2. Inner Loop (variable k):
o For each value of j, the inner loop runs from k = 0 to k = j - 1.
o Therefore, for a given value of j, the inner loop executes j times.
3. Count Increment:
o Each time the inner loop executes, count += 1 is executed once.
Total Number of Executions of count += 1:
To find the total number of times count += 1 is executed, we sum the iterations of the inner loop for
each value of j.
 For j = 0, the inner loop runs 0 times.
 For j = 1, the inner loop runs 1 time.
 For j = 2, the inner loop runs 2 times.
 For j = n - 1, the inner loop runs n - 1 times.
Thus, the total number of executions of count += 1 is the sum of the first n - 1 integers:
Total iterations=0+1+2+3+⋯+(n−1)\text{Total iterations} = 0 + 1 + 2 + 3 + \cdots + (n -
1)Total iterations=0+1+2+3+⋯+(n−1)
This is an arithmetic series with the sum:
n(n−1)2\frac{n(n - 1)}{2}2n(n−1)
Time Complexity:
The total number of iterations is proportional to n(n−1)2\frac{n(n - 1)}{2}2n(n−1), which simplifies to
O(n2)O(n^2)O(n2) in Big O notation.
Conclusion:
The time complexity of the given code is O(n²).
Q6, Write an algorithm to delete all the occurrences of an element say 'n' from given linear linked list.
Algorithm to Delete All Occurrences of an Element from a Linear Linked List
To delete all occurrences of an element n from a linear linked list, we need to traverse the list and
identify nodes that contain the value n. Whenever such a node is found, it should be removed, and the
links should be properly adjusted to maintain the integrity of the list.
Steps:
1. Edge Case: If the list is empty, there’s nothing to delete.
2. Traverse the list from the head.
3. If the head contains the element n, move the head pointer to the next node.
4. For each node after the head, if the current node's data matches n, adjust the links to remove it.
5. Repeat the process until all occurrences of n are removed from the list.
Algorithm
cpp
Copy code
struct Node {
int data;
Node* next;
};
void deleteOccurrences(Node*& head, int n) {
// Handle case where the list is empty
if (head == nullptr) {
return;
}
// Handle removal of occurrences at the head
while (head != nullptr && head->data == n) {
Node* temp = head;
head = head->next; // Move the head pointer
delete temp; // Delete the old head node
}
// Traverse the list to remove occurrences from the rest of the list
Node* current = head;
while (current != nullptr && current->next != nullptr) {
if (current->next->data == n) {
Node* temp = current->next; // Node to delete
current->next = current->next->next; // Skip over the node
delete temp; // Delete the node
} else {
current = current->next; // Move to the next node
}
}
}
Explanation:
1. Edge Case (Empty List):
o If the linked list is empty (head == nullptr), we simply return as there’s nothing to delete.
2. Removing Occurrences at the Head:
o The first while loop checks if the head node contains the element n. If it does, we move
the head pointer to the next node, effectively deleting the current head. This loop
handles multiple occurrences of n at the start of the list.
3. Traversing the List:
o After handling the head, we traverse the rest of the list starting from the new head.
o For each node, we check if the next node's data is equal to n. If it is, we adjust the next
pointer of the current node to skip over the node to be deleted, and then we delete the
node.
o If the next node doesn't contain n, we simply move to the next node.
4. Memory Management:
o Each node that matches n is deleted using the delete operator, freeing the memory
allocated for that node.
Time Complexity:
 The algorithm needs to traverse the list once, checking each node. Therefore, the time
complexity is O(L), where L is the length of the linked list.
Space Complexity:
 The space complexity is O(1) because we are using a constant amount of extra space (only a few
pointers are used). The space required does not depend on the size of the list.
Q7, Define B tree. Draw the B tree of order 5 of the following data : 92, 24, 6, 7, 11, 8, 22, 4, 5, 16, 19,
20, 78
B-tree Definition:
A B-tree is a self-balancing search tree in which nodes can have more than two children. It is designed to
maintain sorted data and allow for efficient insertion, deletion, and search operations. B-trees are
particularly useful for systems that read and write large amounts of data, such as databases and file
systems.
Key Properties of a B-tree:
1. Order (m): The maximum number of children a node can have. It also defines the maximum
number of keys a node can store.
2. Node Structure:

o The root node can have fewer than ⌈m/2⌉ children, but all other nodes must have at
o A node can have up to m - 1 keys and m children.

least ⌈m/2⌉ children.


o Keys within each node are stored in sorted order.
o All leaf nodes are at the same level.
For a B-tree of order 5 (m = 5), each node can hold:
 Maximum of 4 keys (m - 1).
 Maximum of 5 children.
 Minimum of 2 children (for non-root nodes).
 At least 1 key for the root node.
Given Data:
The data to be inserted into the B-tree is:
92, 24, 6, 7, 11, 8, 22, 4, 5, 16, 19, 20, 78
Step-by-Step Construction of the B-tree of Order 5:
We will insert the given data into the B-tree of order 5, ensuring that the tree properties are maintained
during insertion.
Step 1: Insert 92
 The tree is initially empty, so 92 is inserted as the root node.
Tree:
csharp
Copy code
[92]
Step 2: Insert 24
 24 is smaller than 92, so it is inserted into the root node.
Tree:
csharp
Copy code
[24, 92]
Step 3: Insert 6
 6 is smaller than both 24 and 92, so it is inserted into the root node.
Tree:
csharp
Copy code
[6, 24, 92]
Step 4: Insert 7
 7 is greater than 6 but smaller than 24, so it is inserted into the root node.
Tree:
csharp
Copy code
[6, 7, 24, 92]
Step 5: Insert 11
 11 is greater than 7 but smaller than 24, so it is inserted into the root node.
Tree:
csharp
Copy code
[6, 7, 11, 24, 92]
 The root node is full (contains 4 keys), so it splits. The middle element, 11, is promoted to the
new root, and the node is split into two nodes:
o Left child: [6, 7]
o Right child: [24, 92]
Tree after split:
css
Copy code
[11]
/ \
[6, 7] [24, 92]
Step 6: Insert 8
 8 is greater than 7 but smaller than 11, so it is inserted into the left child node [6, 7].
Tree:
css
Copy code
[11]
/ \
[6, 7, 8] [24, 92]
Step 7: Insert 22
 22 is greater than 11 but smaller than 24, so it is inserted into the right child node [24, 92].
Tree:
css
Copy code
[11]
/ \
[6, 7, 8] [22, 24, 92]
Step 8: Insert 4
 4 is smaller than 6, so it is inserted into the left child node [6, 7, 8].
Tree:
css
Copy code
[11]
/ \
[4, 6, 7, 8] [22, 24, 92]
Step 9: Insert 5
 5 is greater than 4 but smaller than 6, so it is inserted into the left child node [4, 6, 7, 8].
Tree:
css
Copy code
[11]
/ \
[4, 5, 6, 7, 8] [22, 24, 92]
 The left child node is full, so it splits. The middle element, 6, is promoted to the root, and the
node is split into two:
o Left child: [4, 5]
o Right child: [7, 8]
Tree after split:
css
Copy code
[6, 11]
/ | \
[4, 5] [7, 8] [22, 24, 92]
Step 10: Insert 16
 16 is greater than 11 but smaller than 22, so it is inserted into the right child node [22, 24, 92].
Tree:
css
Copy code
[6, 11]
/ | \
[4, 5] [7, 8] [16, 22, 24, 92]
Step 11: Insert 19
 19 is greater than 16 but smaller than 22, so it is inserted into the right child node [16, 22, 24,
92].
Tree:
css
Copy code
[6, 11]
/ | \
[4, 5] [7, 8] [16, 19, 22, 24, 92]
 The right child node is full, so it splits. The middle element, 22, is promoted to the root, and the
node is split into two:
o Left child: [16, 19]
o Right child: [24, 92]
Tree after split:
css
Copy code
[6, 11, 22]
/ | | \
[4, 5] [7, 8] [16, 19] [24, 92]
Step 12: Insert 20
 20 is greater than 19 but smaller than 24, so it is inserted into the right child node [16, 19].
Tree:
css
Copy code
[6, 11, 22]
/ | | \
[4, 5] [7, 8] [16, 19, 20] [24, 92]
Step 13: Insert 78
 78 is greater than 22 and 24, so it is inserted into the rightmost child node [24, 92].
Tree:
css
Copy code
[6, 11, 22]
/ | | \
[4, 5] [7, 8] [16, 19, 20] [24, 78, 92]
Final B-tree of Order 5:
After inserting all the elements, the final B-tree is:
css
Copy code
[6, 11, 22]
/ | | \
[4, 5] [7, 8] [16, 19, 20] [24, 78, 92]
Conclusion:
 The B-tree of order 5 has been constructed successfully with the given data.
 The tree is balanced, and all the properties of a B-tree (maximum 4 keys per node, minimum 2
children per internal node) are maintained throughout the construction process.
Q8, Write pseudo code to implement queue using stack i.e. implement insert and delete operation of
queue using push and pop
Pseudo Code to Implement Queue Using Two Stacks
To implement a queue using two stacks, we use two stacks:
1. Stack1 (inStack): This will be used for enqueue (insert) operations.
2. Stack2 (outStack): This will be used for dequeue (delete) operations.
Basic Idea:
 Enqueue Operation (insert): Simply push the element onto inStack.
 Dequeue Operation (delete):
o If outStack is empty, move all elements from inStack to outStack (this reverses the
order of elements).
o Then pop the top element from outStack.
This ensures that the elements are processed in the correct order (FIFO).
Pseudo Code:
plaintext
Copy code
Initialize:
Stack1 (inStack) = empty
Stack2 (outStack) = empty

// Enqueue Operation: Insert element into the queue


Procedure Enqueue(element):
Push element onto inStack

// Dequeue Operation: Remove and return the front element of the queue
Procedure Dequeue():
// If outStack is empty, transfer elements from inStack to outStack
If outStack is empty:
While inStack is not empty:
Pop from inStack and Push to outStack

// If outStack is still empty, return an error (queue is empty)


If outStack is empty:
Return "Queue is empty"
Else:
Pop from outStack and return the value
Explanation of the Operations:
1. Enqueue (insert):
o Simply push the new element onto inStack. This operation takes constant time, O(1).
2. Dequeue (delete):
o If outStack is empty, transfer all elements from inStack to outStack. During this transfer,
the order of elements is reversed, so the element that was first inserted into inStack
ends up at the top of outStack.
o Once the transfer is complete, pop the top element from outStack, which represents
the front of the queue.
o If outStack is not empty, the dequeue operation takes O(1) time.
o The transfer step may take O(n) time when outStack is empty and all elements need to
be moved from inStack.
Time Complexity:
 Enqueue Operation: Each enqueue takes O(1) time because we are just pushing onto the
inStack.
 Dequeue Operation: In the worst case, a dequeue operation can take O(n) time when all
elements are moved from inStack to outStack. However, this occurs at most once per element,
so the amortized time for dequeue operations is O(1).
Space Complexity:
 The space complexity is O(n), where n is the number of elements in the queue, because we are
using two stacks to store the elements.
Q9, A queue can be implemented using single linked list in two ways. One implementation has front at
head and rear at tail of linked list. Other implementation has front at tail and rear at head of linked
list. Which implementation among two is efficient and why?

You might also like