Sorting & Searching Algorithms
From the Data Structures and Algorithms curriculum
TL;DR
Sorting algorithms arrange items in a specific order, making data easier to process, while searching algorithms efficiently locate a specific item within a dataset. Understanding these algorithms is fundamental because they optimize how you organize and find information in almost any computing task. Choosing the right algorithm depends on factors like data size, whether the data is already somewhat sorted, and your performance requirements.
1. The Mental Model
Think of sorting as organizing a messy bookshelf by author, making it much faster to find a specific book later. Searching is then like quickly scanning that organized shelf for "Dune" instead of sifting through every single book.
2. The Core Material
Sorting and searching are two of the most common operations in computer science. They are often discussed together because efficient searching heavily relies on efficiently sorted data.
Sorting Algorithms

Photo by Steve A Johnson on Pexels
Sorting algorithms rearrange a list of elements into a specific order (e.g., numerical, alphabetical). Key characteristics include:
* Time Complexity: How execution time grows with input size (e.g., O(n log n), O(n^2)).
* Space Complexity: How much extra memory the algorithm needs.
* Stability: Whether the relative order of equal elements is preserved.
* In-place: If it sorts the data within the original array without needing a separate one.
Here are a few common sorting algorithms:
- Bubble Sort: Simple, repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. Inefficient for large lists (O(n^2) average and worst-case).
- Selection Sort: Divides the list into two parts: sorted and unsorted. It repeatedly finds the minimum element from the unsorted part and puts it at the end of the sorted part. Also O(n^2).
- Insertion Sort: Builds the final sorted array one item at a time. It iterates through the input elements and inserts each element into its correct position in the already sorted part of the array. Good for small arrays or nearly sorted data (O(n^2) worst-case, O(n) best-case).
- Merge Sort: A "divide and conquer" algorithm. It divides the unsorted list into n sublists, each containing one element, then repeatedly merges sublists to produce new sorted sublists until there is only one sorted sublist remaining. O(n log n) in all cases.
- Quick Sort: Another "divide and conquer" algorithm. It picks an element as a pivot and partitions the given array around the picked pivot. O(n log n) on average, O(n^2) worst-case.
graph TD
A["Start with Unsorted List"] --> B{{"Choose Sorting Algorithm?"}}
B --> C["Bubble/Selection/Insertion Sort (O(n^2))"]
B --> D["Merge/Quick Sort (O(n log n))"]
C --> E{"Done?"}
D --> E
E -- No --> B
E -- Yes --> F["Sorted List Achieved"]
Searching Algorithms

Photo by Steve A Johnson on Pexels
Searching algorithms find the location of a target element within a dataset.
- Linear Search (Sequential Search): Checks each element in the list one by one until the desired element is found or the end of the list is reached. Works on unsorted or sorted data. Time complexity is O(n).
- Binary Search: This is much more efficient but requires the data to be sorted first. It repeatedly divides the search interval in half. If the value of the search key is less than the item in the middle of the interval, you narrow the interval to the lower half. Otherwise, you narrow it to the upper half. Time complexity is O(log n).
Complexity Notation (Big O)

Photo by Vitaly Gariev on Pexels
- O(1) - Constant Time: The operation takes the same amount of time regardless of input size. (e.g., accessing an array element by index).
- O(log n) - Logarithmic Time: Time increases slowly as input size grows. Excellent for large datasets. (e.g., Binary Search).
- O(n) - Linear Time: Time increases proportionally with input size. (e.g., Linear Search).
- O(n log n) - Log-Linear Time: Common for efficient sorting algorithms. (e.g., Merge Sort, Quick Sort average case).
- O(n^2) - Quadratic Time: Time increases quadratically with input size. Becomes very slow for large datasets. (e.g., Bubble Sort, nested loops).
3. Worked Example
Let's illustrate Binary Search.
Suppose you have a sorted list: [2, 5, 8, 12, 16, 23, 38, 56, 72, 91]
You want to find the number 23.
- Initial range:
low = 0,high = 9(indices). - Calculate middle:
mid = (0 + 9) // 2 = 4. Element at index 4 is16. 23 > 16, so you search the right half. New range:low = mid + 1 = 5,high = 9.- Calculate middle:
mid = (5 + 9) // 2 = 7. Element at index 7 is56. 23 < 56, so you search the left half. New range:low = 5,high = mid - 1 = 6.- Calculate middle:
mid = (5 + 6) // 2 = 5. Element at index 5 is23. 23 == 23. Found! The element is at index 5.
Here's a Python implementation:
def binary_search(arr, target):
low = 0
high = len(arr) - 1
while low <= high:
mid = (low + high) // 2
mid_val = arr[mid]
if mid_val == target:
return mid # Target found, return its index
elif mid_val < target:
low = mid + 1 # Target is in the right half
else:
high = mid - 1 # Target is in the left half
return -1 # Target not found
# Test cases
sorted_list = [2, 5, 8, 12, 16, 23, 38, 56, 72, 91]
print(f"Searching for 23: {binary_search(sorted_list, 23)}") # Expected: 5
print(f"Searching for 5: {binary_search(sorted_list, 5)}") # Expected: 1
print(f"Searching for 100: {binary_search(sorted_list, 100)}") # Expected: -1
print(f"Searching for 2: {binary_search(sorted_list, 2)}") # Expected: 0
4. Key Takeaways
- Sorting arranges data in order, while searching locates specific items within data.
- Binary Search is much faster than Linear Search but requires the data to be sorted first.
- Time complexity (Big O notation) helps predict how an algorithm performs with varying input sizes.
- O(n log n) sorting algorithms like Merge Sort and Quick Sort are generally preferred for large datasets over O(n^2) algorithms like Bubble Sort.
- Algorithms have trade-offs in terms of time, space, and implementation complexity.
- Data being sorted is a powerful prerequisite for many efficient data processing techniques.
Common Mistakes to Avoid

Photo by KATRIN BOLOVTSOVA on Pexels
- Trying to use Binary Search on an unsorted list; it will produce incorrect results.
- Overusing simple O(n^2) sorting algorithms for large datasets, leading to slow performance.
- Not considering the implications of different sorting algorithm characteristics like stability or in-place sorting for your specific problem.
- Forgetting to handle edge cases like empty lists or target values not present in the search function.
- Confusing best, average, and worst-case time complexities, especially for algorithms like Quick Sort.
5. Now Try It
Write a Python function called find_smallest_sort(arr) that takes an unsorted list of numbers arr and returns the smallest number in the list using Selection Sort principles (you don't need to sort the whole list, just find the minimum using that logic).
What success looks like: Your function correctly identifies the smallest number for lists of varying lengths, including an empty list (which should return None or raise an error, indicate your choice). For example, find_smallest_sort([5, 2, 8, 1, 9]) should return 1.
Frequently asked about Sorting & Searching Algorithms
Study this next
Get the full Data Structures and Algorithms curriculum
Clone the complete plan to your dashboard for unlimited AI-generated notes, practice quizzes, and a personalised revision schedule.
Create Free Account