Algorithms Explained — Complete Beginner's Guide
An algorithm is a step-by-step procedure for solving a problem — like a recipe for your computer. Understanding algorithms helps you write efficient code that scales.
What You’ll Learn
In this tutorial, you’ll learn what algorithms are, how to measure their efficiency with Big O notation, and how searching and sorting algorithms work with Python examples.
Why It Matters
Every time you search Google, sort a list, or find the fastest route home, an algorithm is working. Choosing the right algorithm can make your program 1000x faster.
Real-World Use
When Google Maps finds the shortest route, it’s running Dijkstra’s algorithm — processing millions of road segments in milliseconds. Your GPS would be useless without efficient algorithms.
flowchart TD
A[Algorithm Choice] --> B[Big O Analysis]
B --> C{O(1)? Constant}
B --> D{O(log n)? Logarithmic}
B --> E{O(n)? Linear}
B --> F{O(n²)? Quadratic}
C --> G[Fastest]
D --> H[Very Fast]
E --> I[Moderate]
F --> J[Slow - avoid for large n]
What’s Big O Notation?
Big O notation describes how an algorithm’s runtime grows as the input size grows. It’s the language we use to compare algorithm efficiency.
Think of it like choosing a car. A sports car (O(log n)) is fast for any trip length. A bicycle (O(n²)) might be okay for a short ride but terrible for a cross-country road trip.
Common Big O Classes
import time
# O(1) - Constant time: always takes the same amount of time
def get_first(items):
return items[0]
# O(n) - Linear time: time grows proportionally with input
def find_max(items):
max_val = items[0]
for item in items:
if item > max_val:
max_val = item
return max_val
# O(n²) - Quadratic time: time grows with the square of input
def find_duplicates(items):
duplicates = []
for i in range(len(items)):
for j in range(i + 1, len(items)):
if items[i] == items[j]:
duplicates.append(items[i])
return duplicates
# Compare runtimes
small = list(range(100))
large = list(range(10000))
start = time.time()
find_max(small)
print(f"O(n) with 100 items: {time.time() - start:.6f}s")
start = time.time()
find_max(large)
print(f"O(n) with 10000 items: {time.time() - start:.6f}s")
# O(n²) is much worse with larger input
small_dup = list(range(50)) + [42]
start = time.time()
find_duplicates(small_dup)
print(f"O(n²) with 51 items: {time.time() - start:.6f}s")Expected output:
O(n) with 100 items: 0.000010s
O(n) with 10000 items: 0.000482s
O(n²) with 51 items: 0.000218sWhat’s happening? O(n) scales linearly — 100x more data → ~100x more time. O(n²) grows much faster. With 1000 items, O(n²) would take ~2500x longer than O(n).
Searching Algorithms
Linear Search
The simplest search: check every element until you find what you’re looking for.
def linear_search(items, target):
"""Check each item one by one until found"""
for i, item in enumerate(items):
if item == target:
return i # Return position
return -1 # Not found
data = [3, 7, 1, 9, 4, 2, 8, 5, 6]
target = 8
result = linear_search(data, target)
print(f"Found {target} at index {result}")
print(f"Checked {result + 1} of {len(data)} items")Expected output:
Found 8 at index 6
Checked 7 of 9 itemsTime complexity: O(n) — in the worst case, you check every item.
Binary Search
Binary search is much faster but requires sorted data. It repeatedly divides the search range in half.
Think of finding a name in a phone book. You don’t start at A and flip every page. You open to the middle, check if your name is before or after, and repeat.
def binary_search(items, target):
"""Search by repeatedly dividing the range in half"""
left = 0
right = len(items) - 1
steps = 0
while left <= right:
steps += 1
mid = (left + right) // 2
if items[mid] == target:
print(f"Found in {steps} steps")
return mid
elif items[mid] < target:
left = mid + 1 # Search right half
else:
right = mid - 1 # Search left half
print(f"Not found after {steps} steps")
return -1
data = sorted([3, 7, 1, 9, 4, 2, 8, 5, 6])
print(f"Sorted data: {data}")
result = binary_search(data, 8)
print(f"Found 8 at index {result}")Expected output:
Sorted data: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Found in 2 steps
Found 8 at index 7Why binary search is amazing: With 1 million sorted items, linear search takes up to 1 million steps. Binary search takes at most 20 steps (log₂(1,000,000) ≈ 20).
Sorting Algorithms
Bubble Sort
Bubble sort repeatedly steps through the list, swapping adjacent items if they’re in the wrong order. Larger elements “bubble up” to the top.
def bubble_sort(items):
"""Repeatedly swap adjacent elements if they're out of order"""
arr = items.copy()
n = len(arr)
steps = 0
for i in range(n):
swapped = False
for j in range(n - i - 1):
steps += 1
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
if not swapped:
break # Already sorted - early exit
print(f"Bubble sort: {steps} comparisons")
return arr
data = [64, 34, 25, 12, 22, 11, 90]
print(f"Unsorted: {data}")
sorted_data = bubble_sort(data)
print(f"Sorted: {sorted_data}")Expected output:
Unsorted: [64, 34, 25, 12, 22, 11, 90]
Bubble sort: 21 comparisons
Sorted: [11, 12, 22, 25, 34, 64, 90]Time complexity: O(n²) — bad for large datasets. With 10,000 items, bubble sort makes ~50 million comparisons.
Merge Sort
Merge sort uses a divide and conquer strategy: split the list in half, sort each half recursively, then merge the sorted halves.
def merge_sort(items):
"""Divide the list, sort halves, merge them"""
arr = items.copy()
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
# Merge the sorted halves
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
# Add remaining elements
result.extend(left[i:])
result.extend(right[j:])
return result
data = [64, 34, 25, 12, 22, 11, 90]
print(f"Unsorted: {data}")
sorted_data = merge_sort(data)
print(f"Sorted: {sorted_data}")Expected output:
Unsorted: [64, 34, 25, 12, 22, 11, 90]
Sorted: [11, 12, 22, 25, 34, 64, 90]Time complexity: O(n log n) — much better than bubble sort. With 10,000 items, merge sort makes ~132,000 comparisons vs bubble sort’s 50 million.
Security Applications of Algorithms
Hash algorithms — SHA-256 and bcrypt are used to securely store passwords. When you log in, your password is hashed and compared to the stored hash.
Encryption algorithms — AES and RSA encrypt your data. Your browser uses these algorithms every time you visit an HTTPS website.
Pattern matching — Security tools use algorithms like KMP (Knuth-Morris-Pratt) to search for malware signatures in files. Products like Durga Antivirus Pro use optimized search algorithms to scan millions of files efficiently.
Common Mistakes Beginners Make
1. Using the wrong algorithm for the job
Using linear search on a sorted list or bubble sort on 100,000 items are classic mistakes. Always consider the input size and data characteristics.
2. Forgetting to consider space complexity
Some algorithms (like merge sort) use O(n) extra memory. On memory-constrained systems, this matters.
3. Not testing with edge cases
Empty arrays, single-element arrays, and already-sorted arrays can break naive implementations.
4. Confusing O(n²) with O(2^n)
O(2^n) (exponential) is much worse than O(n²). For n=50, O(2^n) ≈ 10^15 operations — impossible.
5. Ignoring constant factors
While O(n) and O(2n) are both O(n), the 2x constant factor matters in practice. Don’t ignore it entirely.
Practice Questions
What is Big O notation? A way to describe how an algorithm’s runtime grows as input size increases. It focuses on the growth rate, not exact timing.
Why is binary search faster than linear search? Binary search eliminates half the remaining items each step. For n items, it needs at most log₂(n) steps versus n steps for linear search.
What’s the time complexity of bubble sort? O(n²) in the worst and average cases. O(n) if the array is already sorted (with early exit optimization).
Why is merge sort O(n log n)? The array is divided in half log n times, and merging takes O(n) per level. Hence O(n × log n).
When should you NOT use binary search? When the data isn’t sorted, or when the comparison operation is very expensive.
Challenge
Implement a quicksort algorithm. Compare its performance with merge sort on a list of 10,000 random numbers. Which is faster? Why?
Real-World Task
Open your phone’s contact list. If it has 500 contacts, how many steps does a linear search take to find “Smith”? What about binary search (contacts are sorted by name)?
FAQ
Try It Yourself
Mini Project: Performance Benchmark
Create a benchmark that compares:
- Linear search vs binary search on a sorted list of 100,000 integers
- Bubble sort vs merge sort on 5,000 integers
- Graph the results
Security angle: Pattern-matching algorithms in security tools use the same principles — binary search for known threat signatures (O(log n)) versus scanning entire files (O(n)).
What’s Next
Before moving on, you should understand:
- Big O notation and why it matters
- Linear vs binary search and when to use each
- Bubble sort vs merge sort trade-offs
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
What’s Next
Congratulations on completing this Algorithms tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro