Skip to content
Algorithms Explained — Complete Beginner's Guide

Algorithms Explained — Complete Beginner's Guide

DodaTech Updated Jun 6, 2026 9 min read

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.000218s

What’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 items

Time 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 7

Why 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

  1. 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.

  2. 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.

  3. 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).

  4. 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).

  5. 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

Do I need to memorize algorithms?
No. Understand the concepts — when to use which algorithm and why. You can always look up implementations.
Is Big O the only thing that matters?
No. Constant factors, memory usage, and implementation complexity also matter. Big O is the starting point, not the end.
What’s the fastest sorting algorithm?
There’s no single answer. QuickSort is fastest on average, MergeSort is stable and consistent, TimSort (Python’s built-in sort) excels on real-world data.
Can algorithms be patented?
Pure algorithms (like mathematical formulas) cannot be patented, but their specific implementations can be.
What algorithm does Python’s sort() use?
Timsort — a hybrid of merge sort and insertion sort, designed for real-world data that often contains sorted subsequences.

Try It Yourself

▶ Try It Yourself Edit the code and click Run

Mini Project: Performance Benchmark

Create a benchmark that compares:

  1. Linear search vs binary search on a sorted list of 100,000 integers
  2. Bubble sort vs merge sort on 5,000 integers
  3. 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