CMP160 Data Structure and Algorithms

Data Structure and AlgorithmsUnit 815 min read

Sorting Algorithms: Techniques, Analysis & Applications

Unit 8 of Data Structure and Algorithms covers fundamental sorting techniques (Bubble, Selection, Insertion, Merge, Quick, Heap), their time/space complexity, stability, and real-world applications in Nepalese tech (eSewa, Ncell, Daraz) and global systems (Google, WhatsApp). Includes visual traces of each algorithm, co

Key Concepts and Definitions

What is Sorting?

Sorting is the process of arranging data in a particular order (ascending or descending). It is a fundamental operation in computer science used to organize data for efficient searching, indexing, and analysis.

Types of Sorting Algorithms

Sorting algorithms can be classified into two main categories:

  1. Comparison-based sorts: Compare elements to determine their order (e.g., Bubble Sort, Quick Sort).
  2. Non-comparison-based sorts: Use properties of the data (e.g., Counting Sort, Radix Sort).

1. Basic Sorting Algorithms

1.1 Bubble Sort

How it works: Bubble Sort repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. The process is repeated until the list is sorted.

flowchart TD
    A["Start: Unsorted array"] --> B["Compare A[0] & A[1]"]
    B -->|"If A[0] > A[1]"| C["Swap A[0] & A[1]"]
    B -->|"Else"| D["No swap"]
    C --> E["Move to next pair (A[1] & A[2])"]
    D --> E
    E --> F["Repeat until end of array"]
    F --> G["Pass complete: Largest element bubbled to end"]
    G --> H["Repeat for n-1 passes"]

Time Complexity:

  • Worst/Average Case:
  • Best Case: (if already sorted and optimized)

Space Complexity: (in-place)

Example: Sort the array [5, 3, 8, 4, 2] using Bubble Sort.

arr = [5, 3, 8, 4, 2]
n = len(arr)
for i in range(n):
    for j in range(0, n-i-1):
        if arr[j] > arr[j+1]:
            arr[j], arr[j+1] = arr[j+1], arr[j]
    print(f"After pass {i+1}: {arr}")

Trace:

Pass Array State
1 [3, 5, 4, 2, 8]
2 [3, 4, 2, 5, 8]
3 [3, 2, 4, 5, 8]
4 [2, 3, 4, 5, 8]

Advantages:

  • Simple to understand and implement.
  • No extra memory required.

Disadvantages:

  • Inefficient for large datasets ( time).

Real-world use:

  • eSewa: When sorting transactions by date or amount for user statements.

1.2 Selection Sort

How it works: Selection Sort divides the array into a sorted and unsorted part. It repeatedly selects the smallest (or largest) element from the unsorted part and moves it to the sorted part.

flowchart TD
    A["Start: Unsorted array"] --> B["Find min in unsorted part"]
    B --> C["Swap min with first unsorted element"]
    C --> D["Move boundary between sorted/unsorted right"]
    D --> E["Repeat until entire array is sorted"]

Time Complexity:

  • Worst/Average Case:
  • Best Case: (even if sorted, it still scans the entire array)

Space Complexity:

Example: Sort the array [64, 25, 12, 22, 11].

arr = [64, 25, 12, 22, 11]
n = len(arr)
for i in range(n):
    min_idx = i
    for j in range(i+1, n):
        if arr[j] < arr[min_idx]:
            min_idx = j
    arr[i], arr[min_idx] = arr[min_idx], arr[i]
    print(f"After pass {i+1}: {arr}")

Trace:

Pass Array State
1 [11, 25, 12, 22, 64]
2 [11, 12, 25, 22, 64]
3 [11, 12, 22, 25, 64]
4 [11, 12, 22, 25, 64]

Advantages:

  • Simple to implement.
  • Performs well on small datasets.

Disadvantages:

  • Inefficient for large datasets.

Real-world use:

  • Ncell: Sorting customer call logs by duration to identify peak usage times.

1.3 Insertion Sort

How it works: Insertion Sort builds the final sorted array one item at a time. It takes each element and inserts it into its correct position in the already sorted part of the array.

flowchart TD
    A["Start: Unsorted array"] --> B["Pick next element"]
    B --> C["Compare with sorted elements"]
    C -->|"Insert at correct position"| D["Shift elements if needed"]
    D --> E["Repeat until all elements are sorted"]

Time Complexity:

  • Worst Case:
  • Best Case: (if already sorted)
  • Average Case:

Space Complexity:

Example: Sort the array [12, 11, 13, 5, 6].

arr = [12, 11, 13, 5, 6]
for i in range(1, len(arr)):
    key = arr[i]
    j = i-1
    while j >= 0 and key < arr[j]:
        arr[j+1] = arr[j]
        j -= 1
    arr[j+1] = key
    print(f"After insertion of {key}: {arr}")

Trace:

Step Array State
1 [11, 12, 13, 5, 6]
2 [11, 12, 13, 5, 6]
3 [5, 11, 12, 13, 6]
4 [5, 6, 11, 12, 13]

Advantages:

  • Efficient for small datasets or nearly sorted data.
  • Stable (does not change the order of equal elements).

Disadvantages:

  • Inefficient for large datasets.

Real-world use:

  • Khalti: Sorting transaction records by timestamp for audit trails.

2. Advanced Sorting Algorithms

2.1 Merge Sort

How it works: Merge Sort is a divide-and-conquer algorithm. It divides the array into two halves, recursively sorts each half, and then merges the two sorted halves.

flowchart TD
    A["Divide array into two halves"] --> B["Recursively sort left half"]
    B --> C["Recursively sort right half"]
    C --> D["Merge the two sorted halves"]

Time Complexity:

  • Worst/Average Case:
  • Best Case:

Space Complexity: (requires auxiliary space)

Example: Sort the array [38, 27, 43, 3, 9, 82, 10].

def merge_sort(arr):
    if len(arr) > 1:
        mid = len(arr) // 2
        L = arr[:mid]
        R = arr[mid:]
        merge_sort(L)
        merge_sort(R)
        i = j = k = 0
        while i < len(L) and j < len(R):
            if L[i] < R[j]:
                arr[k] = L[i]
                i += 1
            else:
                arr[k] = R[j]
                j += 1
            k += 1
        while i < len(L):
            arr[k] = L[i]
            i += 1
            k += 1
        while j < len(R):
            arr[k] = R[j]
            j += 1
            k += 1
    return arr

arr = [38, 27, 43, 3, 9, 82, 10]
print("Initial array:", arr)
sorted_arr = merge_sort(arr)
print("Sorted array:", sorted_arr)

Trace:

Initial array: [38, 27, 43, 3, 9, 82, 10]
Sorted array: [3, 9, 10, 27, 38, 43, 82]

Visualization of Merge Sort:

Step 1: Divide [38, 27, 43, 3, 9, 82, 10] into [38, 27, 43] and [3, 9, 82, 10]
Step 2: Divide further until single elements
Step 3: Merge sorted subarrays: [3, 9, 10, 27, 38, 43, 82]

Advantages:

  • Efficient for large datasets ().
  • Stable sort.

Disadvantages:

  • Requires additional memory.

Real-world use:

  • Google: Sorting search results by relevance (part of their indexing pipeline).

2.2 Quick Sort

How it works: Quick Sort is another divide-and-conquer algorithm. It selects a 'pivot' element and partitions the array into two subarrays: elements less than the pivot and elements greater than the pivot. It then recursively sorts the subarrays.

flowchart TD
    A["Choose pivot"] --> B["Partition array into <pivot and >pivot"]
    B --> C["Recursively sort left subarray"]
    B --> D["Recursively sort right subarray"]

Time Complexity:

  • Worst Case: (when pivot is poorly chosen, e.g., smallest or largest element)
  • Average Case:
  • Best Case:

Space Complexity: (due to recursion stack)

Example: Sort the array [10, 7, 8, 9, 1, 5] using Quick Sort (pivot = last element).

def partition(arr, low, high):
    pivot = arr[high]
    i = low - 1
    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]
    arr[i+1], arr[high] = arr[high], arr[i+1]
    return i+1

def quick_sort(arr, low, high):
    if low < high:
        pi = partition(arr, low, high)
        quick_sort(arr, low, pi-1)
        quick_sort(arr, pi+1, high)

arr = [10, 7, 8, 9, 1, 5]
print("Initial array:", arr)
quick_sort(arr, 0, len(arr)-1)
print("Sorted array:", arr)

Trace:

Step Pivot Partitioned Array Recursive Calls
1 5 [1, 5, 8, 9, 10, 7] Sort left: [1], right: [8,9,10,7]
2 7 [1, 5, 7, 9, 10, 8] Sort left: [1,5], right: [9,10,8]
3 8 [1, 5, 7, 8, 10, 9] Sort left: [1,5,7], right: [10,9]
4 9 [1, 5, 7, 8, 9, 10] Base case reached

Advantages:

  • Fastest in practice for large datasets.
  • In-place sorting (minimal extra memory).

Disadvantages:

  • Worst-case if pivot is poorly chosen.
  • Unstable sort.

Real-world use:

  • WhatsApp: Sorting message timestamps for chat history display.

2.3 Heap Sort

How it works: Heap Sort uses a binary heap data structure. It first builds a max-heap from the input data, then repeatedly extracts the maximum element and rebuilds the heap.

flowchart TD
    A["Build max-heap from array"] --> B["Extract max element (root)"]
    B --> C["Place max at end of array"]
    C --> D["Reduce heap size and heapify"]
    D --> E["Repeat until heap is empty"]

Time Complexity:

  • Worst/Average Case:
  • Best Case:

Space Complexity:

Example: Sort the array [4, 10, 3, 5, 1] using Heap Sort.

def heapify(arr, n, i):
    largest = i
    l = 2*i + 1
    r = 2*i + 2
    if l < n and arr[l] > arr[largest]:
        largest = l
    if r < n and arr[r] > arr[largest]:
        largest = r
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)

def heap_sort(arr):
    n = len(arr)
    for i in range(n//2 - 1, -1, -1):
        heapify(arr, n, i)
    for i in range(n-1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]
        heapify(arr, i, 0)

arr = [4, 10, 3, 5, 1]
print("Initial array:", arr)
heap_sort(arr)
print("Sorted array:", arr)

Trace:

Step Heap State (Max at Root) Extracted Max Remaining Array
1 [10, 5, 3, 4, 1] 10 [4, 5, 3, 1]
2 [5, 4, 3, 1] 5 [4, 3, 1]
3 [4, 1, 3] 4 [3, 1]
4 [3, 1] 3 [1]
5 [1] 1 []

Advantages:

  • Guaranteed time.
  • In-place sorting.

Disadvantages:

  • Not stable.
  • Slower in practice than Quick Sort due to poor cache performance.

Real-world use:

  • NEPSE: Sorting stock prices for daily trading reports.

3. Comparison of Sorting Algorithms

Algorithm Best Case Average Case Worst Case Space Complexity Stable? Notes
Bubble Sort Yes Simple, inefficient
Selection Sort No Minimizes swaps
Insertion Sort Yes Efficient for small datasets
Merge Sort Yes Stable, good for linked lists
Quick Sort No Fastest in practice
Heap Sort No In-place, not stable

4. Stability in Sorting

A sorting algorithm is stable if it preserves the relative order of equal elements. For example:

  • Stable: Bubble Sort, Insertion Sort, Merge Sort.
  • Unstable: Selection Sort, Quick Sort, Heap Sort.

Example: Sort the array [3, 1, 4, 1, 5, 9, 2, 6] where the second 1 must appear before the first 1 in the output if they are equal.

flowchart TD
    A["Unsorted: [3, 1, 4, 1, 5, 9, 2, 6]"] --> B["Stable Sort: [1, 1, 2, 3, 4, 5, 6, 9]"]
    B --> C["Unstable Sort: [1, 1, 2, 3, 4, 5, 6, 9] (order of 1's may change)"]

Real-world use:

  • Daraz: Sorting product listings by price while maintaining the order of products with the same price (e.g., based on user ratings).

5. Choosing the Right Sorting Algorithm

Scenario Recommended Algorithm
Small datasets (<100 elements) Insertion Sort
Nearly sorted data Insertion Sort
General-purpose sorting Quick Sort
Stable sort required Merge Sort
Memory constraints Heap Sort or Quick Sort
External sorting (large data) Merge Sort

In the Real World

  1. eSewa:

    • Idea Used: Merge Sort or Quick Sort.
    • How: eSewa sorts transactions by date and amount for generating user statements and financial reports. Efficient sorting ensures quick access to transaction history, improving user experience.
  2. Ncell:

    • Idea Used: Heap Sort or Quick Sort.
    • How: Ncell uses sorting to prioritize customer calls based on duration or urgency. For example, calls with longer durations are sorted to identify peak usage times for network optimization.
  3. Daraz:

    • Idea Used: Stable Sorting (e.g., Merge Sort).
    • How: Daraz sorts product listings by price while maintaining the order of products with identical prices (e.g., based on user ratings or reviews). This ensures consistency in search results.
  4. Google Search:

    • Idea Used: Advanced sorting algorithms (e.g., variants of Quick Sort or Merge Sort).
    • How: Google sorts search results by relevance, which involves complex ranking algorithms that rely on efficient sorting to quickly retrieve the most relevant results for users.
  5. NEPSE (Nepal Stock Exchange):

    • Idea Used: Heap Sort or Merge Sort.
    • How: NEPSE sorts stock prices and trading volumes to generate daily reports and rankings. Efficient sorting helps traders and analysts quickly access the most up-to-date information.
  6. Khalti Transactions:

    • Idea Used: Insertion Sort (for small datasets) or Merge Sort (for large datasets).
    • How: Khalti sorts transactions by timestamp to create audit trails and detect fraudulent activities. Sorting ensures that transactions are processed in chronological order for accurate record-keeping.

Exam Tip

  1. Understand Time Complexity:

    • Memorize the time complexities of each algorithm (e.g., Bubble Sort is , Merge Sort is ). Examiners often ask to identify the best algorithm for a given scenario.
  2. Practice Traces:

    • Be able to manually trace the steps of any sorting algorithm (e.g., show how Bubble Sort sorts [4, 2, 5, 1]). This is a common exam question.
  3. Stability Matters:

    • Know which algorithms are stable (e.g., Merge Sort, Insertion Sort) and why stability is important in certain applications.
  4. Comparison Tables:

    • Expect questions comparing algorithms based on time complexity, space complexity, and stability. Create a comparison table like the one above to revise quickly.
  5. Real-world Applications:

    • Relate sorting algorithms to real-world systems (e.g., "Which sorting algorithm would you use for sorting transactions in eSewa?"). This tests your understanding of practical constraints (e.g., stability, speed).
  6. Code Implementation:

    • Be ready to write pseudocode or actual code for any sorting algorithm. Examiners may ask for implementations of Bubble Sort, Quick Sort, or Merge Sort.
  7. Optimizations:

    • Know basic optimizations (e.g., early termination in Bubble Sort if no swaps occur in a pass). This can be the difference between a correct and incorrect answer.

Based on the PU BE Computer (PU) syllabus for Data Structure and Algorithms (CMP160), unit 8.

Discussion

Loading…