Abstract flowing gradient in deep indigo and blue tones, smooth and luminous, evoking a modern digital learning atmosphere

Computer Science and programming articles. We do not sell courses.

Implementing a segment tree for range queries and updates

When developers in Sydney or Melbourne tackle competitive programming problems on platforms like LeetCode, they often bump into a recurring puzzle: how to update a single value in an array and then sum, find the minimum, or compute some other function over a range of indices, all in logarithmic time. A naive approach loops through the affected range, which becomes painfully slow for large inputs. A smarter structure, the segment tree, solves exactly this class of problem and forms a cornerstone of any interview preparation kit shared across Australian university coding clubs, an approach that mirrors the practical orientation described on the about page of this site.

Behind the scenes, a segment tree is a binary tree where each node represents an interval of the underlying array. The leaves hold individual elements, while internal nodes store aggregated information about their children. This layout allows queries and updates to descend through the tree, touching only a logarithmic number of nodes regardless of how wide the queried range becomes. The structure is particularly handy when a program needs to handle many interleaved operations, a pattern that surfaces in everything from financial tickers processed in Australian offices to genomics pipelines run by national research teams.

Why segment trees matter for range operations

The motivation for this data structure comes from a simple observation. If an application performs many range queries on a static array, prefix sums computed once at the start can answer each query in constant time. The trouble begins the moment values change, because updating a single element forces the prefix array to be rebuilt. Segment trees offer a middle ground: they handle updates and queries in logarithmic time, accepting a modest constant overhead in exchange for flexibility.

In practice, the structure shines in scenarios such as tracking the maximum temperature across a range of weather stations, summing transaction values within sliding windows of stock data, or counting active users in intervals of session logs. The same techniques power machine learning feature stores that maintain rolling statistics over high-volume event streams. Australian software teams at firms like Atlassian in Sydney frequently use variants of this structure when building analytics dashboards that need to refresh in real time, and the UNSW computing society often features range queries as a recurring theme in its competitive programming problem set.

Comparing segment trees to alternatives

Before committing to one structure, it helps to compare what is on offer. The table below summarises four common options against the criteria that matter most when designing a solution.

Structure Build time Range query Point update Range update Memory
Plain array O(n) O(n) O(1) O(n) O(n)
Prefix sums O(n) O(1) O(n) O(n) O(n)
Fenwick tree O(n) O(log n) O(log n) O(log n) O(n)
Segment tree O(n) O(log n) O(log n) O(log n) O(2n)
Lazy segment tree O(n) O(log n) O(log n) O(log n) O(4n)

The plain array and prefix sums are easy to code but stumble once updates enter the picture. Fenwick trees, also known as binary indexed trees, are more compact but cannot handle non-invertible operations such as range minimum queries without extra tricks. A segment tree, especially in its lazy form, handles arbitrary associative functions and range updates with the same logarithmic guarantee, which is why it is the preferred tool in textbook treatments and in the practice problems circulated by Melbourne and Monash coding groups.

Building the tree from an array

Construction starts by allocating an array of size four times the input length, a safe upper bound that works for any n. A recursive helper function places the input values at the leaf positions and then fills parent nodes with the combined value of their two children. For a sum operation, each parent simply stores the sum of the left and right children. For a minimum, the parent stores whichever child is smaller.

The recursion bottoms out when a node represents a single index, at which point the value from the original array is copied into the tree. Because the recursion visits every node exactly once, the total build cost is linear in the size of the tree, which is itself proportional to the number of input elements. This one-time setup cost is a worthwhile investment when the structure is then used for thousands of queries, a common pattern in log analysis tools maintained by operations teams running continuous monitoring across cloud workloads.

Performing range queries

To answer a range query, the algorithm walks down the tree, splitting the requested interval at node boundaries. Each recursive call checks whether its node interval lies completely inside, completely outside, or partially overlaps the query range. When the node is fully inside, the stored aggregate is returned immediately, skipping the descent. When it is fully outside, a neutral value is returned that does not affect the combination. When the intervals only partially overlap, the algorithm recurses into both children and merges their results.

The merge step applies the same associative function used during construction, such as addition, minimum, maximum, or a custom operator. Because the tree has a height of O(log n), at most a logarithmic number of nodes are visited, which keeps each query snappy. For students preparing for coding interviews across the region, mastering this pattern pays off repeatedly, since range queries appear in roughly one in five problems on most platforms.

Point and range updates

Updating a single element follows a similar descent from the root to the affected leaf. At each internal node on the path, the stored aggregate is recomputed from the two children, so the change propagates upward in logarithmic time. This is the point update operation and it is the basic building block for more advanced use cases.

To support range updates, the lazy propagation technique introduces a secondary array that records pending operations at each node. When a range update covers an entire node interval, the pending operation is stored in the lazy array and the node's aggregate is updated immediately. The stored operation is pushed down to the children only when a later query or update requires it. This deferral keeps the work bounded to O(log n) per operation, a property that has made lazy segment trees a staple in problems featuring massive datasets, such as the ones processed by data engineering teams handling mining telemetry in Western Australia.

Complexity and performance characteristics

The theoretical analysis of segment trees is reassuringly clean. Building takes O(n) time, each query runs in O(log n), each point update runs in O(log n), and each range update with lazy propagation also runs in O(log n). Space usage is O(4n) for the standard version, which is generous but still linear in the input size. These bounds hold for any associative merge function, making the structure adaptable to a wide range of problems.

Constant factors matter in real applications. In C, careful use of iterative loops instead of recursion can cut overhead, especially in deeply nested trees. Cache locality also plays a role, since the node indices follow a predictable pattern that maps well to array storage. For the kind of bulk processing done by Australian financial institutions, where a single query might span years of transaction history, these optimisations translate directly into faster response times and lower compute bills on cloud infrastructure.

Implementation in C and practical recommendations

The following pseudocode outlines a complete implementation in C, drawing on patterns from the c programming resources on this site. The functions handle construction, range sum queries, point updates, and range updates with lazy propagation. In production code, the same logic extends to other operations by swapping the merge function, and the recursive base cases can be unrolled for tighter performance.

Practical recommendations for using segment trees effectively: