Home/Data Structures & Algorithms

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.
Please briefly explain why you feel this question should be reported.
Please briefly explain why you feel this answer should be reported.
Please briefly explain why you feel this user should be reported.
Master core and advanced data structures and algorithms with production-grade implementations, hardware-conscious optimizations, and real-world system designs.
pgvector: HNSW index build fails with out-of-memory or high swap: How to tune maintenance_work_mem & parallel workers
Direct Technical Solution: Unlike IVFFlat (which partitions vector spaces with k-means centroids), HNSW (Hierarchical Navigable Small World) constructs a multi-layer proximity graph in physical RAM during build time. For 2,000,000 vectors with 1,536 dimensions, the raw vectors alone occupy 2,000,000Read more
Direct Technical Solution: Unlike IVFFlat (which partitions vector spaces with k-means centroids), HNSW (Hierarchical Navigable Small World) constructs a multi-layer proximity graph in physical RAM during build time. For 2,000,000 vectors with 1,536 dimensions, the raw vectors alone occupy
2,000,000 × 1,536 × 4 bytes = 12.28 GB. Adding the HNSW graph neighbor connectivity lists (with m=16) increases total build RAM requirement to approximately 18 to 22 GB.1. The Formula to Calculate Required `maintenance_work_mem`
2. PostgreSQL Configuration Tuning
Temporarily allocate sufficient RAM to the session before triggering the index build, and utilize parallel CPU worker cores to accelerate graph edge exploration:
3. When to Use IVFFlat vs HNSW
Why does std::views::filter on temporary containers trigger undefined behavior and dangling references in C++20?
Direct Technical Solution: In C++20, range view adaptors (like std::views::filter, std::views::transform, and std::views::take) are strictly non-owning view wrappers. They do not duplicate or take ownership of the underlying container; they only store iterators pointing directly into the underlyingRead more
Direct Technical Solution: In C++20, range view adaptors (like
std::views::filter,std::views::transform, andstd::views::take) are strictly non-owning view wrappers. They do not duplicate or take ownership of the underlying container; they only store iterators pointing directly into the underlying sequence.In your code,
getTemperatures()returns a temporarystd::vector<int>by value. At the semicolon ending the initialization expressionauto warm_days = getTemperatures() | ...;, the temporary vector reaches the end of its full-expression lifetime and is immediately destructed. Consequently, the iterators stored insidewarm_daysbecome dangling pointers into deallocated stack/heap memory, causing undefined behavior upon iteration.PyTorch RuntimeError: CUDA out of memory: Why torch.cuda.empty_cache() fails & how to fix fragmentation
Direct Technical Solution: torch.cuda.empty_cache() releases only cached (unallocated) blocks back to the CUDA driver; it never frees memory occupied by active tensors (weights, optimizer states, computation graph nodes). Calling it inside your training loop hurts performance because CUDA must constRead more
Direct Technical Solution:
torch.cuda.empty_cache()releases only cached (unallocated) blocks back to the CUDA driver; it never frees memory occupied by active tensors (weights, optimizer states, computation graph nodes). Calling it inside your training loop hurts performance because CUDA must constantly re-allocate OS memory via costly system calls.1. Root Cause: PyTorch Allocator Memory Fragmentation
Look closely at your error message:
18.21 GiB allocated+4.80 GiB reserved. PyTorch had nearly 5 GB of memory held in its internal caching allocator, but it was split into scattered, non-contiguous memory chunks. When a tensor required 512 MiB of contiguous VRAM, the allocator failed to find a single chunk large enough.2. The Modern Fix: Expandable Segments (PyTorch 2.0+)
The definitive solution in modern PyTorch is activating virtual memory management via the
expandable_segmentsflag. This instructs CUDA to map physical memory pages to a contiguous virtual memory space, virtually eliminating memory fragmentation:3. The 4 Golden Rules to Prevent CUDA OOM
- Detach Loss Values: Never accumulate raw tensor losses:
- Use Automatic Mixed Precision (AMP): Halve activation memory using native BF16/FP16:
- Gradient Accumulation: Instead of a batch size of 64 that OOMs, use a micro-batch size of 16 and accumulate gradients across 4 backward steps:
- Zero Gradients with `set_to_none=True`:
See lesstotal_loss += lossretains the entire backward computation graph in VRAM! Always usetotal_loss += loss.item().optimizer.zero_grad(set_to_none=True)deallocates memory instead of zeroing tensors with zeros of equal size.Next.js 15: Error: Route used “params” without awaiting it (Asynchronous Page Props Fix)
Direct Technical Solution: In Next.js 15, dynamic route parameters (params) and search parameters (searchParams) transitioned from synchronous plain JavaScript objects to native Promises. This breaking change was implemented to support React 19 Server Components and the new Partial Prerendering (PPRRead more
Direct Technical Solution: In Next.js 15, dynamic route parameters (
params) and search parameters (searchParams) transitioned from synchronous plain JavaScript objects to native Promises. This breaking change was implemented to support React 19 Server Components and the new Partial Prerendering (PPR) model, where the server renders the static page skeleton before dynamic parameters resolve.1. Server Component Migration (Async/Await)
In all Next.js 15 Server Components (the default in the
app/directory), typeparamsas aPromiseand explicitlyawaitit before accessing any key:2. Client Component Migration (React 19 `use()` Hook)
If your page file uses
"use client", you cannot make the component functionasync. Instead, unwrap theparamsPromise using React 19’s nativeReact.use()hook:3. Comparison: Next.js 14 vs Next.js 15
{ slug: string }(Synchronous Object)Promise<{ slug: string }>(Async Promise)params.slug)const { slug } = await params;useParams()hookuse(params)oruseParams()Task Scheduler with Cooldowns: Closed-form mathematical formula vs Priority Queue simulation
The Task Scheduler problem is a masterclass in recognizing that the most frequent task dictates the entire schedule structure. 1. Deriving the Formula Visually Suppose our tasks are [A, A, A, B, B, C] with cooldown n = 2. Task A appears most frequently ($count = 3$). Between each A, there must be atRead more
The Task Scheduler problem is a masterclass in recognizing that the most frequent task dictates the entire schedule structure.
1. Deriving the Formula Visually
Suppose our tasks are
[A, A, A, B, B, C]with cooldownn = 2.Task
Aappears most frequently ($count = 3$). Between eachA, there must be at leastn = 2cooldown slots:Notice the structure:
max_freq - 1full frames.n + 1(the task itself plus itsncooldown slots).2. The Closed-Form Equation
Let
max_freqbe the highest frequency of any task, andmax_countbe how many tasks tie for that highest frequency (for example, if both A and B appear 3 times,max_count = 2).What if there are so many other tasks that no CPU idle slots are needed?
If you have tons of diverse tasks (e.g.
[A, A, B, B, C, D, E, F, G, H]), they easily fill up all idle slots, and the CPU never needs to idle at all! In that case, the answer is simplylen(tasks).Therefore, the global answer is simply:
Clean Python 3.12 Implementation (0 CPU Simulation Cycles!)
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(N)to count task frequencies. The mathematical formula itself evaluates inO(1)time!O(1)auxiliary space, because the alphabet size is bounded by 26 English uppercase letters.Daily Temperatures: How to use an Index-Tracking Monotonic Stack for next warmer day in O(N)
This problem is the cleanest introductory template for the Monotonic Decreasing Stack pattern. Let's look at why storing indices unlocks the distance calculation. 1. The Mental Model Imagine people waiting in line holding temperature tickets. If temperatures are dropping: [73, 71, 69], nobody has foRead more
This problem is the cleanest introductory template for the Monotonic Decreasing Stack pattern. Let’s look at why storing indices unlocks the distance calculation.
1. The Mental Model
Imagine people waiting in line holding temperature tickets. If temperatures are dropping:
[73, 71, 69], nobody has found a warmer day yet! So everyone has to stay waiting in line.Now, a warm day arrives:
72!69sees72 > 69. Their wait is over! They step out of line.71sees72 > 71. Their wait is over! They step out of line.73sees72 < 73.72is not warm enough for them! The person with73stays waiting in line, and the day with72joins the line behind them.2. Why Store Indices Instead of Values?
If you only push temperature numbers (e.g.
69) onto the stack, when a warmer day72pops69, you know that a warmer day happened, but you don’t know how many days elapsed!By pushing the array index
prev_dayonto the stack:You calculate the exact time difference in $O(1)$ and write directly to the output array!
Clean Python 3.12 Implementation
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(N). Every index is pushed onto the stack once and popped at most once. Total operations: $le 2N$.O(N)for the stack in the worst-case of strictly decreasing temperatures (e.g.[100, 90, 80, 70]).Lowest Common Ancestor: Why Binary Lifting in O(log N) beats naive DFS in high-scale DAGs
When you have multiple online LCA queries on a static tree, the gold standard is Binary Lifting (used in compiler dominance frontiers, distributed network routing, and Git commit histories). 1. The Core Idea: Powers of 2 Parent Jumps Instead of storing only a node's immediate parent (which forces yoRead more
When you have multiple online LCA queries on a static tree, the gold standard is Binary Lifting (used in compiler dominance frontiers, distributed network routing, and Git commit histories).
1. The Core Idea: Powers of 2 Parent Jumps
Instead of storing only a node’s immediate parent (which forces you to step up the tree one node at a time in $O(N)$), what if every node stored its ancestor at distance $2^0, 2^1, 2^2, 2^3, dots, 2^k$?
We define a 2D table:
up[u][i] = the (2^i)-th ancestor of node u.The state transition is pure dynamic programming:
In English: ‘To jump $2^i$ steps up from $u$, first jump $2^{i-1}$ steps up to reach intermediate node $v$, and then from $v$ jump another $2^{i-1}$ steps!’ ($2^{i-1} + 2^{i-1} = 2^i$).
2. Answering an LCA Query in 2 Steps
To find the LCA of nodes
uandv:depth[u] < depth[v], swap them. Use binary powers to jumpuupwards untildepth[u] == depth[v]in $O(log N)$ steps. Ifu == v, they were on the same branch → returnu!uandvupwards together using the largest possible power of 2 such that their ancestors are still different (up[u][i] != up[v][i]). When no more jumps can be made, their immediate parent (up[u][0]) is their Lowest Common Ancestor!Clean C++20 Implementation
Complexity Breakdown
- Preprocessing Time:
- Preprocessing Memory:
- Per Query Time: Strictly
See lessO(N log N)via a single DFS pass.O(N log N)to store the jump table.O(log N). For $N = 1,000,000$, $log_2(1,000,000) pprox 20$ operations. You can evaluate 50,000 queries in a fraction of a second!Subarray Sum Equals K: Why Two Pointers fails with negative numbers and Hash Map is mandatory
This is a classic trap that catches even intermediate developers. Let's see why two pointers collapse and why prefix math is the ultimate solution. 1. Why Two Pointers Fails on Negative Numbers A sliding window relies on a fundamental monotonic invariant: If the current sum is too small, expanding tRead more
This is a classic trap that catches even intermediate developers. Let’s see why two pointers collapse and why prefix math is the ultimate solution.
1. Why Two Pointers Fails on Negative Numbers
A sliding window relies on a fundamental monotonic invariant:
The moment you introduce negative numbers, this invariant is destroyed! Expanding the right pointer might add
-10, making the sum smaller. Shrinking the left pointer might drop-5, making the sum larger. You can no longer make greedy left/right decisions!2. The Prefix Sum Invariant
Let
prefix[i]be the cumulative sum from index 0 toi.The sum of any contiguous subarray from index
j + 1toiis given by:sum(j+1 ... i) = prefix[i] - prefix[j].We want this subarray sum to equal
k:The Breakthrough: As you iterate through the array maintaining a running prefix sum
curr_sum, you simply ask the hash map: ‘How many times have we already seen a prefix sum equal tocurr_sum - kin the past?’Every time you find that value in the hash map, you have found a valid subarray that sums exactly to
k!Clean Python 3.12 Implementation
Why prefix_counts[0] = 1 is Critical
If you forget
prefix_counts[0] = 1, any subarray that starts at index 0 and sums tok(e.g.nums = [3, ...], k = 3) will producecurr_sum = 3, and look forcurr_sum - k = 0in the map. Without the base case, it would fail to count that valid subarray!Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(N). Single pass through the array withO(1)hash map lookups.O(N)to store prefix sum frequencies.Minimum Window Substring: Why an integer frequency array beats HashMap in low-latency parsers
Minimum Window Substring is the crown jewel of sliding window problems. The difference between a junior solution and a staff engineer solution comes down to how window validation is tracked. 1. The Trap: Comparing Two Hash Maps In naive implementations, developers maintain two hash maps: target_counRead more
Minimum Window Substring is the crown jewel of sliding window problems. The difference between a junior solution and a staff engineer solution comes down to how window validation is tracked.
1. The Trap: Comparing Two Hash Maps
In naive implementations, developers maintain two hash maps:
target_countsandwindow_counts. Whenever the window slides, they loop through the keys oftarget_countsto see if the window is valid. That turns an $O(N)$ algorithm into $O(N cdot |Sigma|)$ with heavy hash table overhead!2. The Staff Engineer Pattern: Single Vector + Deficit Counter
We can optimize this down to bare metal with two simple tricks:
required): We setrequired = len(t). When expanding the window with pointerr, ifcounts[s[r]] > 0, that character was actively needed, so we decrementrequired--. Whenrequired == 0, the window is 100% valid! We don’t have to check any other variables!Clean Python 3.12 Implementation
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(|s| + |t|). Therightpointer advances|s|times. Theleftpointer advances at most|s|times. Total pointer advances =2|s|.O(1)auxiliary space. Exactly 128 integers on the stack.How to find the Single Number when all others appear 3 times using a Digital Logic State Machine?
This problem is a masterpiece of digital circuit design translated into software code. Let's design the state machine from first principles. 1. The Three States of a Bit For any bit position, as we scan numbers in the array, how many times can we see a 1? Seen 0 times → Count = 0 Seen 1 timeRead more
This problem is a masterpiece of digital circuit design translated into software code. Let’s design the state machine from first principles.
1. The Three States of a Bit
For any bit position, as we scan numbers in the array, how many times can we see a
1?To represent 3 distinct states (0, 1, and 2), we need 2 bits of memory! Let’s name them:
twos(the high bit)ones(the low bit)2. The Truth Table
When a new bit
xarrives from the current number:3. Deriving the Logic Gates
From the truth table:
ones = (ones ^ x) & (~twos)twos = (twos ^ x) & (~ones)When the full array has been scanned:
ones!Clean Python 3.12 Implementation
Complexity Breakdown
- Time Complexity:
- Space Complexity:
See lessO(N). We touch each number once with 4 single-cycle bitwise operations.O(1). Exactly two integer variables living in registers.