Hello! Welcome to the first lesson of your course on widely used algorithms. Given your background in software engineering, our goal is to build a solid, practical understanding of these concepts, starting with the fundamentals.
This first lesson is foundational. We'll tackle the language used to describe and compare algorithms: Big-O notation. By the end of this session, you'll be able to look at a piece of code—whether it's a simple loop or a recursive function—and determine its time complexity. This skill is the bedrock for making informed decisions about which algorithmic approach is best for a given problem.
1. Why Not Just Time It? The Motivation for Big-O
As an experienced developer, you know that measuring performance is key. A natural first thought might be to just run the code and time it with a stopwatch or a system clock. However, this approach has some significant drawbacks.
To understand why we need a more abstract way to measure efficiency, let's start by defining Big-O and exploring the problems with direct time measurement.
This brief text from a blog post on Codeforces provides a concise, formal definition of Big-O notation and distinguishes between theoretical and experimental analysis.
Please read the sections titled 'Introduction' and 'Big O Notation'. Focus on understanding why direct timing ('Experimental Analysis') is unreliable and how Big-O ('Theoretical Analysis') provides a more stable alternative by focusing on the relationship between input size and the number of operations.
Now, let's see these concepts in action. The following video segment does an excellent job of demonstrating the variability of direct timing and introducing Big-O as a way to analyze how an algorithm's performance scales.
The Complete Guide to Big O Notation & Complexity Analysis for Algorithms: Part 1 of 2
This video from Coderbyte will walk you through the motivation for Big-O and why we can't just rely on measuring execution time in seconds.
Watch the beginning of the video from 00:00 to 05:16. Pay close attention to the experiment the presenter runs to show how execution time varies even for the same code on the same machine.
To summarize, Big-O notation gives us a way to talk about the efficiency of an algorithm by focusing on two key ideas:
- Growth Rate: It describes how the runtime (or memory usage) grows as the input size,
n, gets larger. - Worst-Case Scenario: It typically describes the upper bound on performance, ensuring we're prepared for the most demanding situations.
This focus on the asymptotic behavior—how the algorithm behaves for very large inputs—is why we can ignore hardware and other environmental factors.
2. Analyzing Iterative Algorithms
Now let's apply this to code. We'll start with iterative algorithms, which primarily use loops. The process involves "counting" the number of elementary operations an algorithm performs relative to the input size n.
First, let's visualize the common complexity classes. This graph illustrates why we're so concerned with finding efficient algorithms. An O(n^2) algorithm becomes impractical much faster than an O(n log n) or O(n) one as the input size grows.

Basic Rules and Simplification
When calculating Big-O, we follow two simple but powerful rules to keep our analysis clean and focused on what matters most: the dominant growth factor.
The next segment of the Coderbyte video provides a clear, step-by-step guide to analyzing a simple loop and introduces the rules for simplification.
The Complete Guide to Big O Notation & Complexity Analysis for Algorithms: Part 1 of 2
This part of the video breaks down the analysis of a simple iterative function and then details the two essential rules for simplifying Big-O expressions.
Watch from 05:16 to 16:30. The first part (until 09:48) analyzes an O(n) function. The second part (from 09:48) explicitly explains the 'Product Rule' (dropping constants) and the 'Sum Rule' (keeping the largest term). These are fundamental.
Let's recap those crucial rules:
- Drop Constant Factors: We don't care about the difference between
O(2n)andO(n). For largen, the linear growth is the important part. Both are simplified toO(n). - Drop Non-Dominant Terms: In an expression like
O(n^2 + n + 100), then^2term will grow much faster thannor100. Asnbecomes large, the other terms become insignificant. We simplify this toO(n^2).
Applying the Rules to Code
With these rules, we can analyze more complex structures like sequential and nested loops.
- Sequential Loops: Two separate loops, one running
ntimes and the othermtimes, result inO(n + m)complexity. We add the complexities. - Nested Loops: A loop running
ntimes that contains another loop runningntimes results inO(n * n) = O(n^2)complexity. We multiply the complexities.
Let's watch the final segment on iterative analysis, where these rules are applied to slightly more complex code snippets.
The Complete Guide to Big O Notation & Complexity Analysis for Algorithms: Part 1 of 2
Here, the concepts are put together to analyze code with nested loops and multiple function calls.
Watch from 16:30 to 22:19. Observe how the presenter identifies the complexity of each part of the code (each loop) and then combines them using the sum and product rules to find the overall complexity.
3. Analyzing Recursive Algorithms
Analyzing recursive functions can seem trickier, but the principle is the same. We need to determine the total number of operations performed. This is typically done by considering two things:
- Work per call: How many operations are performed inside a single call to the function (ignoring the recursive calls)?
- Number of calls: How many times does the function call itself?
The total time complexity is the product of these two factors.
Let's look at a concrete example. The image below breaks down the analysis for a recursive function that calculates x to the power of n.

This linear recursion is straightforward. What if the function doesn't just decrement by 1? The following video explains how to analyze recursive functions, including one where the problem size is halved at each step—a pattern that you'll see again in algorithms like binary search.
The Complete Guide to Big O Notation & Complexity Analysis for Algorithms: Part 1 of 2
This final video segment explains how to think about the complexity of recursive functions, considering both the number of recursive calls and the work done in each.
Watch from 25:58 to 30:24. Notice the difference in analysis between a function that calls itself with n-1 versus n-2. This illustrates why only the scaling factor matters, as both result in O(n) complexity.
The key insight for recursion is to visualize the "call tree":
- A function that calls itself once (e.g., on
n-1) creates a simple chain of calls. The depth of this chain determines the complexity. If the depth isn, complexity isO(n). - A function that halves the input at each step (e.g., calling itself on
n/2) creates a chain of depthlog n. This is logarithmic time,O(log n), which is extremely efficient. - A function that calls itself twice (e.g.,
foo(n/2); foo(n/2);) creates a branching tree. This is common in "divide and conquer" algorithms, which we will study later.
Conclusion
In this lesson, we've established the "why" and "how" of Big-O notation for analyzing algorithm time complexity.
Key Takeaways:
- Big-O measures scalability: It describes how an algorithm's runtime changes as the input size (
n) grows. - Focus on the worst case: This gives us a reliable upper bound on performance.
- Simplify for clarity: We drop constant factors and non-dominant terms to focus on the most significant part of the complexity expression.
- Iterative Analysis: Analyze loops. Sequential loops add (
O(n+m)), while nested loops multiply (O(n^2)). - Recursive Analysis: Analyze the recursion depth and the work done at each step. A function calling itself with
n-1is typicallyO(n), while one usingn/2is oftenO(log n).
You now have the fundamental toolset to evaluate the efficiency of any algorithm you encounter.
In our next lesson, we will refine this understanding by looking at Big-Omega (Ω) and Big-Theta (Θ) notations. These will allow us to describe the best-case and average-case performance, giving us a more complete picture than Big-O's worst-case view alone.