1] Radix Sort Explanation
Radix Sort is a non-comparative sorting algorithm that sorts numbers by processing individual digits.
Here’s a step-by-step explanation using the given numbers: 54, 43, 47, 59, 36, 60, 55, 26, 35, 31.
Step-by-Step Sorting
Initial Array: 54, 43, 47, 59, 36, 60, 55, 26, 35, 31
Sorting by Least Significant Digit (Unit Place):
Buckets:
0: 60
1: 31
2:
3: 43
4: 54
5: 55, 35
6: 36, 26
7: 47
8:
9: 59
Result: 60, 31, 43, 54, 55, 35, 36, 26, 47, 59
Sorting by Most Significant Digit (Tens Place):
Buckets:
0:
1:
2: 26
3: 31, 35, 36
4: 43, 47
5: 54, 55
6: 60
7:
8:
9: 59
Result: 26, 31, 35, 36, 43, 47, 54, 55, 59, 60
When to Use Radix Sort
Radix Sort is particularly useful when:
The range of the numbers (or keys) is not significantly larger than the number of items.
The keys are integers or strings where the length of the keys is relatively small.
You need a stable sort (preserves the relative order of records with equal keys).
Time Complexity
Best Case: (O(n \cdot k))
Average Case: (O(n \cdot k))
Worst Case: (O(n \cdot k))
Where:
(n) is the number of elements.
(k) is the number of digits in the largest number.
2] Singly Linked List Insertion
A singly linked list is a data structure where each node contains data and a reference (or link) to the
next node in the sequence. Here’s how you can insert a node into a singly linked list and count the
number of nodes.
Insertion in a Singly Linked List
Pseudo Code for Insertion at the Beginning:
function insertAtHead(head, value):
newNode = Node(value)
newNode.next = head
head = newNode
return head
Pseudo Code for Insertion at the End:
function insertAtEnd(head, value):
newNode = Node(value)
if head == NULL:
head = newNode
return head
current = head
while current.next != NULL:
current = current.next
current.next = newNode
return head
Pseudo Code for Insertion After a Given Node:
function insertAfter(node, value):
if node == NULL:
return
newNode = Node(value)
newNode.next = node.next
node.next = newNode
Counting Nodes in a Singly Linked List
Pseudo Code for Counting Nodes:
function countNodes(head):
count = 0
current = head
while current != NULL:
count = count + 1
current = current.next
return count
Example Implementation in Python
Here’s how you might implement these operations in Python:
Python
class Node:
def __init__(self, data):
self.data = data
self.next = None
class SinglyLinkedList:
def __init__(self):
self.head = None
def insert_at_head(self, value):
new_node = Node(value)
new_node.next = self.head
self.head = new_node
def insert_at_end(self, value):
new_node = Node(value)
if self.head is None:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
def count_nodes(self):
count = 0
current = self.head
while current:
count += 1
current = current.next
return count
# Example usage:
sll = SinglyLinkedList()
sll.insert_at_head(10)
sll.insert_at_end(20)
sll.insert_at_end(30)
print("Number of nodes:", sll.count_nodes())
# Output: Number of nodes: 3
3] Doubly Linked List
A doubly linked list is a type of linked list in which each node contains three fields:
Data: The value stored in the node.
Next: A pointer to the next node in the sequence.
Prev: A pointer to the previous node in the sequence.
Advantages of Doubly Linked List
Bidirectional Traversal: You can traverse the list in both forward and backward directions.
Easier Deletion: Deleting a node is easier because you have a reference to the previous node.
Flexibility: More flexible in terms of operations like insertion and deletion compared to singly linked
lists.
Disadvantages of Doubly Linked List
More Memory: Requires more memory per node because of the additional pointer.
Complexity: More complex to implement and manage due to the additional pointers.
Insertion Algorithm
Insertion at the Beginning:
function insertAtHead(head, value):
newNode = Node(value)
newNode.next = head
newNode.prev = NULL
if head != NULL:
head.prev = newNode
head = newNode
return head
Insertion at the End:
function insertAtEnd(head, value):
newNode = Node(value)
if head == NULL:
newNode.prev = NULL
head = newNode
return head
last = head
while last.next != NULL:
last = last.next
last.next = newNode
newNode.prev = last
return head
Insertion After a Given Node:
function insertAfter(node, value):
if node == NULL:
return
newNode = Node(value)
newNode.next = node.next
node.next = newNode
newNode.prev = node
if newNode.next != NULL:
newNode.next.prev = newNode
Traversal Algorithm
Forward Traversal:
function traverseForward(head):
current = head
while current != NULL:
print(current.data)
current = current.next
Backward Traversal:
function traverseBackward(tail):
current = tail
while current != NULL:
print(current.data)
current = current.prev
Example Implementation in Python
Here’s how you might implement these operations in Python:
Python
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class DoublyLinkedList:
def __init__(self):
self.head = None
def insert_at_head(self, value):
new_node = Node(value)
new_node.next = self.head
if self.head is not None:
self.head.prev = new_node
self.head = new_node
def insert_at_end(self, value):
new_node = Node(value)
if self.head is None:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
new_node.prev = last
def traverse_forward(self):
current = self.head
while current:
print(current.data, end=' ')
current = current.next
print()
def traverse_backward(self):
current = self.head
while current and current.next:
current = current.next
while current:
print(current.data, end=' ')
current = current.prev
print()
# Example usage:
dll = DoublyLinkedList()
dll.insert_at_head(10)
dll.insert_at_end(20)
dll.insert_at_end(30)
dll.traverse_forward() # Output: 10 20 30
dll.traverse_backward() # Output: 30 20 10
4] Generalized Linked List for Multivariable Polynomial Representation
A generalized linked list is a versatile data structure that can be used to represent complex structures
like multivariable polynomials. In this context, each node can represent a term of the polynomial,
and the list can handle multiple variables and their respective exponents.
Node Structure
Each node in a generalized linked list for a multivariable polynomial typically contains:
Coefficient: The numerical multiplier of the term.
Exponent: The power to which the variable is raised.
Next: A pointer to the next term (node) in the polynomial.
Variable: The variable associated with the term (optional, depending on implementation).
Here’s a simplified structure in pseudo code:
class Node:
coefficient: int
exponent: int
variable: char
next: Node
sublist: Node
Example
Consider the multivariable polynomial: ( 3x^2y + 4xy^2 + 5 ).
This polynomial can be represented using a generalized linked list as follows:
Top Level List:
Node 1: Coefficient = 3, Exponent = 2, Variable = ‘x’, Sublist -> Node 2
Node 2: Coefficient = 4, Exponent = 1, Variable = ‘x’, Sublist -> Node 3
Node 3: Coefficient = 5, Exponent = 0, Variable = ‘x’, Sublist = NULL
Sublists:
Node 1 Sublist: Coefficient = 1, Exponent = 1, Variable = ‘y’, Next = NULL
Node 2 Sublist: Coefficient = 1, Exponent = 2, Variable = ‘y’, Next = NULL
Advantages
Flexibility: Can represent polynomials with multiple variables and varying degrees.
Dynamic Size: Can easily grow or shrink as terms are added or removed.
Efficient Operations: Insertion, deletion, and traversal operations are efficient.
Disadvantages
Memory Overhead: Requires additional memory for pointers.
Complexity: More complex to implement and manage compared to simple arrays or singly linked
lists.
Algorithm for Insertion and Traversal
Insertion Algorithm:
function insertTerm(head, coefficient, exponent, variable):
newNode = Node(coefficient, exponent, variable)
if head == NULL:
head = newNode
return head
current = head
while current.next != NULL:
current = current.next
current.next = newNode
return head
Traversal Algorithm:
function traversePolynomial(head):
current = head
while current != NULL:
print(current.coefficient, current.variable, "^", current.exponent)
if current.sublist != NULL:
traversePolynomial(current.sublist)
current = current.next
Example Implementation in Python
Here’s how you might implement these operations in Python:
Python
class Node:
def __init__(self, coefficient, exponent, variable):
self.coefficient = coefficient
self.exponent = exponent
self.variable = variable
self.next = None
self.sublist = None
class Polynomial:
def __init__(self):
self.head = None
def insert_term(self, coefficient, exponent, variable):
new_node = Node(coefficient, exponent, variable)
if self.head is None:
self.head = new_node
else:
current = self.head
while current.next:
current = current.next
current.next = new_node
def traverse(self, node=None):
if node is None:
node = self.head
while node:
print(f"{node.coefficient}{node.variable}^{node.exponent}", end=" ")
if node.sublist:
self.traverse(node.sublist)
node = node.next
print()
# Example usage:
poly = Polynomial()
poly.insert_term(3, 2, 'x')
poly.head.sublist = Node(1, 1, 'y')
poly.insert_term(4, 1, 'x')
poly.head.next.sublist = Node(1, 2, 'y')
poly.insert_term(5, 0, 'x')
poly.traverse()
5] Node Structure of Singly Linked List
A singly linked list is a linear data structure where each element (node) points to the next node in the
sequence. Each node contains two parts:
Data: The value stored in the node.
Next: A pointer/reference to the next node in the list.
Node Structure:
class Node:
data: int
next: Node
Node Structure of Circular Singly Linked List
A circular singly linked list is similar to a singly linked list, but the last node points back to the first
node, forming a circle. This means there is no NULL at the end of the list.
Node Structure:
class Node:
data: int
next: Node
In a circular singly linked list, the next pointer of the last node points to the head of the list.
Pseudocode for Concatenation of Two Singly Linked Lists
To concatenate two singly linked lists, we need to link the last node of the first list to the head of the
second list.
Pseudocode:
function concatenateLists(head1, head2):
if head1 == NULL:
return head2
if head2 == NULL:
return head1
current = head1
while current.next != NULL:
current = current.next
current.next = head2
return head1
Example Implementation in Python
Here’s how you might implement these operations in Python:
Python
class Node:
def __init__(self, data):
self.data = data
self.next = None
class SinglyLinkedList:
def __init__(self):
self.head = None
def append(self, data):
new_node = Node(data)
if not self.head:
self.head = new_node
return
last = self.head
while last.next:
last = last.next
last.next = new_node
def concatenate(self, other_list):
if not self.head:
self.head = other_list.head
return
if not other_list.head:
return
last = self.head
while last.next:
last = last.next
last.next = other_list.head
def display(self):
current = self.head
while current:
print(current.data, end=" -> ")
current = current.next
print("None")
# Example usage:
list1 = SinglyLinkedList()
list1.append(1)
list1.append(2)
list1.append(3)
list2 = SinglyLinkedList()
list2.append(4)
list2.append(5)
list2.append(6)
list1.concatenate(list2)
list1.display() # Output: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> None
This code defines a Node class and a SinglyLinkedList class with methods to append nodes,
concatenate two lists, and display the list. The concatenate method links the last node of the first list
to the head of the second list.
6] Necessity of Prefix and Postfix Notation
Prefix (Polish) Notation and Postfix (Reverse Polish) Notation are ways to write expressions without
the need for parentheses to denote operation order. This makes them particularly useful for
computer processing.
Advantages:
• No Parentheses Needed: The order of operations is unambiguous without parentheses.
• Efficient Computation: Easier for computers to evaluate using stacks.
• Consistent Order: Operators appear in a consistent order relative to their operands.
Postfix Evaluation Using Stack
Algorithm for Postfix Evaluation:
1. Initialize an empty stack.
2. Read the postfix expression from left to right.
3. For each element:
o If the element is an operand, push it onto the stack.
o If the element is an operator, pop the top two elements from the stack, apply the
operator, and push the result back onto the stack.
4. The result will be the only element left in the stack.
Example Evaluation
Given postfix expression: A B C * D E F ^ / G * - H +
With values: A=7, B=2, C=5, D=17, E=3, F=4, G=3, H=6
Step-by-Step Evaluation:
1. Initial Stack: []
2. Read A (7): [7]
3. Read B (2): [7, 2]
4. Read C (5): [7, 2, 5]
5. Read *: Pop 5 and 2, push 10: [7, 10]
6. Read D (17): [7, 10, 17]
7. Read E (3): [7, 10, 17, 3]
8. Read F (4): [7, 10, 17, 3, 4]
9. Read ^: Pop 4 and 3, push 81: [7, 10, 17, 81]
10. Read /: Pop 81 and 17, push 0.2099: [7, 10, 0.2099]
11. Read G (3): [7, 10, 0.2099, 3]
12. Read *: Pop 3 and 0.2099, push 0.6297: [7, 10, 0.6297]
13. Read -: Pop 0.6297 and 10, push 9.3703: [7, 9.3703]
14. Read H (6): [7, 9.3703, 6]
15. Read +: Pop 6 and 9.3703, push 15.3703: [7, 15.3703]
Final Result: 15.3703
Pseudocode for Postfix Evaluation
function evaluatePostfix(expression):
stack = createEmptyStack()
for token in expression:
if token is operand:
stack.push(token)
else if token is operator:
operand2 = stack.pop()
operand1 = stack.pop()
result = applyOperator(token, operand1, operand2)
stack.push(result)
return stack.pop()
function applyOperator(operator, operand1, operand2):
if operator == '+':
return operand1 + operand2
if operator == '-':
return operand1 - operand2
if operator == '*':
return operand1 * operand2
if operator == '/':
return operand1 / operand2
if operator == '^':
return operand1 ^ operand2
This pseudocode outlines the process of evaluating a postfix expression using a stack, ensuring
efficient and accurate computation.
7] Step-by-Step Conversion from Infix to Postfix Using Stack
To convert an infix expression to a postfix expression, we use a stack to help manage the operators
and ensure they are applied in the correct order. Here’s how you can convert the given infix
expression (( ( a / ( b - c + d ) ) * ( e - a ) * c )) to postfix notation step-by-step:
Infix Expression: (( ( a / ( b - c + d ) ) * ( e - a ) * c ))
Steps:
1. Initialize an empty stack for operators and an empty list for the output (postfix expression).
2. Scan the infix expression from left to right.
Detailed Steps:
1. Read ‘(’: Push to stack.
o Stack: [‘(’]
o Output: []
2. Read ‘(’: Push to stack.
o Stack: [‘(’, ‘(’]
o Output: []
3. Read ‘a’: Append to output.
o Stack: [‘(’, ‘(’]
o Output: [‘a’]
4. Read ‘/’: Push to stack.
o Stack: [‘(’, ‘(’, ‘/’]
o Output: [‘a’]
5. Read ‘(’: Push to stack.
o Stack: [‘(’, ‘(’, ‘/’, ‘(’]
o Output: [‘a’]
6. Read ‘b’: Append to output.
o Stack: [‘(’, ‘(’, ‘/’, ‘(’]
o Output: [‘a’, ‘b’]
7. Read ‘-’: Push to stack.
o Stack: [‘(’, ‘(’, ‘/’, ‘(’, ‘-’]
o Output: [‘a’, ‘b’]
8. Read ‘c’: Append to output.
o Stack: [‘(’, ‘(’, ‘/’, ‘(’, ‘-’]
o Output: [‘a’, ‘b’, ‘c’]
9. Read ‘+’: Pop ‘-’ from stack (lower precedence) and push ‘+’.
o Stack: [‘(’, ‘(’, ‘/’, ‘(’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’]
o Stack: [‘(’, ‘(’, ‘/’, ‘(’, ‘+’]
10. Read ‘d’: Append to output.
o Stack: [‘(’, ‘(’, ‘/’, ‘(’, ‘+’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’]
11. Read ‘)’: Pop and append until ‘(’.
o Stack: [‘(’, ‘(’, ‘/’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’]
12. Read ‘)’: Pop and append until ‘(’.
o Stack: [‘(’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’]
13. Read ‘*’: Push to stack.
o Stack: [‘(’, ‘*’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’]
14. Read ‘(’: Push to stack.
o Stack: [‘(’, ‘*’, ‘(’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’]
15. Read ‘e’: Append to output.
o Stack: [‘(’, ‘*’, ‘(’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’]
16. Read ‘-’: Push to stack.
o Stack: [‘(’, ‘*’, ‘(’, ‘-’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’]
17. Read ‘a’: Append to output.
o Stack: [‘(’, ‘*’, ‘(’, ‘-’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’]
18. Read ‘)’: Pop and append until ‘(’.
o Stack: [‘(’, ‘*’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’, ‘-’]
19. Read ‘*’: Pop and append ‘*’ (same precedence).
o Stack: [‘(’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’, ‘-’, ‘*’]
o Stack: [‘(’, ‘*’]
20. Read ‘c’: Append to output.
o Stack: [‘(’, ‘*’]
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’, ‘-’, ‘*’, ‘c’]
21. Read ‘)’: Pop and append until ‘(’.
o Stack: []
o Output: [‘a’, ‘b’, ‘c’, ‘-’, ‘d’, ‘+’, ‘/’, ‘e’, ‘a’, ‘-’, ‘', ‘c’, '’]
Final Postfix Expression:
abc-d+/ea-*c*
This step-by-step process ensures that the operators are applied in the correct order, respecting their
precedence and associativity rules.
8] Fibonacci Search
Fibonacci Search is a comparison-based search algorithm that uses Fibonacci numbers to divide the
array into smaller sections. It is similar to binary search but uses Fibonacci numbers to determine the
split points, which can be more efficient in certain scenarios.
Key Points:
• Divide and Conquer: Like binary search, it divides the array into smaller parts.
• Fibonacci Numbers: Uses Fibonacci numbers to determine the split points.
• Efficiency: Can be more efficient than binary search in some cases, especially when dealing
with non-uniform memory access.
Pseudocode for Fibonacci Search
function fibonacciSearch(arr, x, n):
fibMMm2 = 0 // (m-2)'th Fibonacci number
fibMMm1 = 1 // (m-1)'th Fibonacci number
fibM = fibMMm2 + fibMMm1 // m'th Fibonacci number
// fibM is the smallest Fibonacci number greater than or equal to n
while (fibM < n):
fibMMm2 = fibMMm1
fibMMm1 = fibM
fibM = fibMMm2 + fibMMm1
offset = -1
// While there are elements to be inspected
while (fibM > 1):
i = min(offset + fibMMm2, n-1)
// If x is greater than the value at index fibMMm2, cut the subarray array from offset to i
if (arr[i] < x):
fibM = fibMMm1
fibMMm1 = fibMMm2
fibMMm2 = fibM - fibMMm1
offset = i
// If x is less than the value at index fibMMm2, cut the subarray after i+1
else if (arr[i] > x):
fibM = fibMMm2
fibMMm1 = fibMMm1 - fibMMm2
fibMMm2 = fibM - fibMMm1
// Element found
else:
return i
// Comparing the last element with x
if (fibMMm1 and arr[offset + 1] == x):
return offset + 1
// Element not found
return -1
Example: Search for 54 in the List
Given list: {46, 34, 63, 31, 54, 67, 76, 68, 73, 32}
1. Sort the List: {31, 32, 34, 46, 54, 63, 67, 68, 73, 76}
2. Initialize Fibonacci Numbers: fibMMm2 = 0, fibMMm1 = 1, fibM = 1
3. Find the smallest Fibonacci number greater than or equal to 10:
o fibMMm2 = 1, fibMMm1 = 1, fibM = 2
o fibMMm2 = 1, fibMMm1 = 2, fibM = 3
o fibMMm2 = 2, fibMMm1 = 3, fibM = 5
o fibMMm2 = 3, fibMMm1 = 5, fibM = 8
o fibMMm2 = 5, fibMMm1 = 8, fibM = 13
4. Search Process:
o offset = -1
o i = min(-1 + 5, 9) = 4 (compare arr[4] with 54)
o arr[4] = 54, which matches the search element.
Result: The element 54 is found at index 4 in the sorted list.
Analysis of Fibonacci Search
• Time Complexity: The time complexity of Fibonacci search is (O(\log n)), similar to binary
search1.
• Space Complexity: The space complexity is (O(1)) as it uses a constant amount of extra
space.
• Advantages:
o Efficient for large datasets.
o Can be more cache-friendly than binary search due to non-uniform memory access
patterns.
• Disadvantages:
o Requires the array to be sorted.
o Slightly more complex to implement compared to binary search.
Fibonacci search is particularly useful in scenarios where the cost of comparison is high, and the
array is large and sorted.
9] Quick Sort Algorithm
Quick Sort is a highly efficient sorting algorithm based on the divide-and-conquer principle. It works
by selecting a ‘pivot’ element from the array and partitioning the other elements into two sub-
arrays, according to whether they are less than or greater than the pivot. The sub-arrays are then
sorted recursively.
Pseudocode for Quick Sort
function quickSort(arr, low, high):
if low < high:
pivotIndex = partition(arr, low, high)
quickSort(arr, low, pivotIndex - 1)
quickSort(arr, pivotIndex + 1, high)
function partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j = low to high - 1:
if arr[j] <= pivot:
i=i+1
swap arr[i] with arr[j]
swap arr[i + 1] with arr[high]
return i + 1
Example: Sorting the Array {55, 79, 81, 75, 93, 23, 66, 21}
Let’s go through the steps of sorting this array using Quick Sort:
1. Initial Array: {55, 79, 81, 75, 93, 23, 66, 21}
2. First Call: quickSort(arr, 0, 7)
o Pivot: 21
o Partition: {21, 79, 81, 75, 93, 23, 66, 55}
o Pivot Index: 0
3. Recursive Calls:
o quickSort(arr, 0, -1) (Base case, no action)
o quickSort(arr, 1, 7)
4. Second Call: quickSort(arr, 1, 7)
o Pivot: 55
o Partition: {21, 23, 81, 75, 93, 79, 66, 55}
o Pivot Index: 2
5. Recursive Calls:
o quickSort(arr, 1, 1) (Base case, no action)
o quickSort(arr, 3, 7)
6. Third Call: quickSort(arr, 3, 7)
o Pivot: 55
o Partition: {21, 23, 55, 75, 93, 79, 66, 81}
o Pivot Index: 2
7. Recursive Calls:
o quickSort(arr, 3, 1) (Base case, no action)
o quickSort(arr, 3, 7)
8. Fourth Call: quickSort(arr, 3, 7)
o Pivot: 81
o Partition: {21, 23, 55, 66, 75, 79, 81, 93}
o Pivot Index: 6
9. Recursive Calls:
o quickSort(arr, 3, 5)
o quickSort(arr, 7, 7) (Base case, no action)
10. Fifth Call: quickSort(arr, 3, 5)
o Pivot: 79
o Partition: {21, 23, 55, 66, 75, 79, 81, 93}
o Pivot Index: 5
11. Recursive Calls:
o quickSort(arr, 3, 4)
o quickSort(arr, 6, 5) (Base case, no action)
12. Sixth Call: quickSort(arr, 3, 4)
o Pivot: 75
o Partition: {21, 23, 55, 66, 75, 79, 81, 93}
o Pivot Index: 4
13. Recursive Calls:
o quickSort(arr, 3, 3) (Base case, no action)
o quickSort(arr, 5, 4) (Base case, no action)
Final Sorted Array: {21, 23, 55, 66, 75, 79, 81, 93}
Analysis of Quick Sort
• Time Complexity:
o Best Case: (O(n \log n))
o Average Case: (O(n \log n))
o Worst Case: (O(n^2)) (occurs when the smallest or largest element is always chosen
as the pivot)
• Space Complexity: (O(\log n)) due to the recursive stack space.
Quick Sort is generally faster in practice compared to other (O(n \log n)) algorithms like Merge Sort
and Heap Sort, especially for large datasets. However, its performance can degrade to (O(n^2)) if the
pivot selection is poor.
10] Selection Sort Algorithm
Selection Sort is a simple comparison-based sorting algorithm. It works by repeatedly finding the
minimum element from the unsorted part of the array and moving it to the beginning.
Pseudocode for Selection Sort
function selectionSort(arr):
n = length(arr)
for i from 0 to n-1:
minIndex = i
for j from i+1 to n:
if arr[j] < arr[minIndex]:
minIndex = j
swap arr[i] with arr[minIndex]
Example: Sorting the Array {33, 66, 11, 24, 65, 32, 86, 45}
Let’s go through the steps of sorting this array using Selection Sort:
Initial Array: {33, 66, 11, 24, 65, 32, 86, 45}
Pass 1:
Find the minimum element from index 0 to 7: 11
Swap 11 with the element at index 0
Array: {11, 66, 33, 24, 65, 32, 86, 45}
Pass 2:
Find the minimum element from index 1 to 7: 24
Swap 24 with the element at index 1
Array: {11, 24, 33, 66, 65, 32, 86, 45}
Pass 3:
Find the minimum element from index 2 to 7: 32
Swap 32 with the element at index 2
Array: {11, 24, 32, 66, 65, 33, 86, 45}
Pass 4:
Find the minimum element from index 3 to 7: 33
Swap 33 with the element at index 3
Array: {11, 24, 32, 33, 65, 66, 86, 45}
Pass 5:
Find the minimum element from index 4 to 7: 45
Swap 45 with the element at index 4
Array: {11, 24, 32, 33, 45, 66, 86, 65}
Pass 6:
Find the minimum element from index 5 to 7: 65
Swap 65 with the element at index 5
Array: {11, 24, 32, 33, 45, 65, 86, 66}
Pass 7:
Find the minimum element from index 6 to 7: 66
Swap 66 with the element at index 6
Array: {11, 24, 32, 33, 45, 65, 66, 86}
Pass 8:
The last element is already in place
Array: {11, 24, 32, 33, 45, 65, 66, 86}
Final Sorted Array: {11, 24, 32, 33, 45, 65, 66, 86}
Time and Space Complexity
Time Complexity:
Best Case: (O(n^2))
Average Case: (O(n^2))
Worst Case: (O(n^2))
The time complexity is (O(n^2)) in all cases because it always performs (n(n-1)/2) comparisons,
regardless of the initial order of the array.
Space Complexity: (O(1))
Selection Sort is an in-place sorting algorithm, meaning it requires only a constant amount of
additional memory space.