Back to Blog
Data structures and Big O through real application problems
Computer ScienceAlgorithmsData Structures

Data structures and Big O through real application problems

Understand arrays, hash maps, queues, heaps, and graphs by tracing the work an application performs.

Big O is useful when it explains why a feature becomes slow as its input grows. Memorizing that a lookup is “fast” is less useful than identifying which lookup happens, how often it repeats, and what assumptions make the estimate valid.

Consider a dashboard that joins activity records to user profiles. This small example connects algorithm analysis with a decision developers make in ordinary application code.

Count repeated work before optimizing syntax

Suppose there are n activities and m users. For every activity, scanning the user list can inspect up to m profiles. Repeating that scan for all activities gives O(n × m) work in the worst case.

const enriched = activities.map(activity => ({
  ...activity,
  user: users.find(user => user.id === activity.userId),
}));

If both lists contain 10,000 entries, the upper bound involves roughly 100 million profile comparisons. This is an illustrative count, not a runtime benchmark: early matches, engine behavior, and hardware affect actual elapsed time.

Build an index once and reuse it:

const usersById = new Map(users.map(user => [user.id, user]));
const enriched = activities.map(activity => ({
  ...activity,
  user: usersById.get(activity.userId) ?? null,
}));

Under the usual expected constant-time hash lookup model, building the index takes O(m), and the joins take O(n), for O(n + m) expected work. The tradeoff is O(m) additional index space. This estimate is an algorithmic model, not a guarantee that every JavaScript engine implements every Map operation in strict constant time.

Duplicate IDs also change behavior: find selects the first match, while repeated Map keys keep the last assigned value. An optimization that changes which user appears is a correctness bug. Establish uniqueness or deliberately preserve the original rule.

Understand what the notation leaves out

Big O describes an asymptotic upper bound; it does not specify milliseconds. An O(n) implementation can lose to another O(n) implementation because it allocates many objects, performs expensive parsing, or reads from slower memory.

Distinguish worst-case, average-case, and amortized analysis. Appending to a dynamically resized array may occasionally require copying its contents. Spread across many appends, that resizing cost can still yield constant amortized work per append in the standard dynamic-array model. An individual append is not thereby guaranteed constant latency.

Always name the input. A function with a fixed loop of ten iterations is constant with respect to list length, but work inside each iteration may still depend on the size of a string or document.

Select a structure for its operations

Arrays are useful for ordered collections and indexed access. A sorted array enables binary search, but maintaining sorted order can require shifting elements on insertion. A hash map is suited to key-based lookups; it does not automatically support efficient range queries.

A queue models first-in-first-out work. If a breadth-first traversal repeatedly removes the first element of an array, front removal can introduce shifting costs. A head index avoids that repeated operation:

const queue = [start];
const seen = new Set([start]);
let head = 0;

while (head < queue.length) {
  const node = queue[head++];
  for (const neighbor of adjacency.get(node) ?? []) {
    if (!seen.has(neighbor)) {
      seen.add(neighbor);
      queue.push(neighbor);
    }
  }
}

Marking nodes when enqueuing prevents duplicate queue entries. With adjacency lists and expected constant-time set membership, this traversal takes O(V + E) for the reachable vertices and edges. The array retains processed entries until released; a long-lived queue needs compaction or a ring buffer to avoid retaining unnecessary memory.

Use heaps when the question is about priority

Suppose a log processor needs the ten largest durations from a million records. Sorting all records costs O(n log n) in a standard comparison-sort model. A min-heap of size k can retain the largest k values in O(n log k) time with O(k) extra storage. The root is the smallest retained value, so a new value replaces it only when larger.

For ten records, ordinary sorting may be clearer and entirely sufficient. For a continuous stream, bounded storage becomes a stronger reason to choose a heap. Requirements determine whether the additional implementation complexity earns its place.

Think of relationships as graphs

Dependency resolution, navigation paths, and social connections are graph problems. A tree is a special structure with a unique path between nodes; arbitrary dependency graphs can have shared descendants and cycles. Recursive traversal without a visited set may revisit work or never terminate.

Choose breadth-first search for shortest paths measured by edge count in an unweighted graph. Weighted paths require different reasoning; breadth-first search does not minimize arbitrary costs. A dependency ordering needs cycle detection because a cyclic dependency cannot have a valid topological order.

A repeatable analysis habit

Write down the required operations and expected sizes. Estimate time and extra memory. Check duplicates, missing values, empty inputs, and cycles. Then benchmark realistic distributions rather than only a tiny happy path.

For structured study, MIT’s Introduction to Algorithms course connects data structures, correctness, and complexity. In application work, keep the same discipline: explain why the result is correct before explaining why it is fast.

Related Posts

JavaScript concurrency: the event loop, races, and backpressure

JavaScript concurrency: the event loop, races, and backpressure

Understand async execution, avoid stale responses, and limit parallel work without blocking the interface.

Computer ScienceWeb DevelopmentJavaScript
Read More
PostgreSQL: indexes, query plans, and correct transactions

PostgreSQL: indexes, query plans, and correct transactions

A practical database guide to designing indexes, reading execution plans, and preventing concurrent update mistakes.

Computer ScienceDatabasesPostgreSQL
Read More