Constant
Reading time: 25 minutes
Constant time complexity, written as O(1), describes operations whose execution time does not depend on the input size. Whether you have 10 elements or 10 million, the operation takes roughly the same amount of time. This is the fastest complexity class and the ideal for operations you perform frequently.
What O(1) means
An O(1) operation completes in a fixed amount of time regardless of how much data you have. The "1" does not mean "one operation" or "one millisecond." It means the time is constant, bounded by some fixed value that does not grow with input size.
More formally, an algorithm is O(1) if there exists a constant c such that the running time is at most c for all input sizes n.
The formal definition states that f(n) = O(1) if there exist positive constants c and n₀ such that f(n) ≤ c for all n ≥ n₀. This means the function is bounded above by some constant, regardless of input size.
The mechanics of constant time
To understand why certain operations are O(1), you need to understand how computers access memory. Modern computers use random access memory (RAM), which allows any memory location to be accessed in the same amount of time. This is in contrast to sequential access (like magnetic tape) where accessing later elements takes longer.
When data is stored at a known address, retrieving it requires:
Calculate the memory address
Request data from that address
Receive the data
Each of these steps takes a fixed amount of time, regardless of how much other data exists in memory.
Examples of O(1) operations
Array access by index
Accessing an array element by its index is the canonical O(1) operation:
Arrays store elements in contiguous memory. Given the starting address and element size, the computer calculates the exact memory location with simple arithmetic: address = start + (index × elementSize). This calculation takes constant time regardless of array size.
The formula works because arrays guarantee contiguous storage. Element 0 is at the starting address, element 1 is one element-size later, and so on. This predictable layout enables O(1) access to any element.
Array assignment by index
Setting an array element is also O(1):
Like reading, writing requires calculating an address and performing a memory operation. The array length does not affect how long this takes.
Hash table operations
Hash tables (JavaScript objects, Maps, Sets) provide O(1) average-case operations:
Hash tables compute a hash of the key to find the storage location directly. Instead of searching through all stored items, they jump directly to where the item should be.
The hash function converts any key into a number (the hash). This number, after some modular arithmetic, gives an array index. Looking up a key means:
Compute the hash (O(1) for fixed-size keys)
Convert to array index (O(1))
Access array element (O(1))
Collisions can degrade performance, but with a good hash function and appropriate load factor, operations remain O(1) on average.
Stack push and pop
Stacks support O(1) insertion and removal at one end:
Push and pop operate on the end of the array, requiring no shifting of elements. The operation directly accesses the last position.
Queue operations with proper implementation
Queues support O(1) operations when implemented correctly:
Using an object with head/tail pointers avoids the O(n) cost of array shift operations.
Arithmetic and logical operations
Basic computations on fixed-size values are O(1):
These operations work on numbers of fixed size (typically 64 bits). The CPU executes them in a single instruction or a small fixed number of instructions.
However, arithmetic on arbitrary-precision numbers (BigInt) is NOT O(1). Adding two n-digit numbers requires O(n) digit-by-digit addition.
Linked list head operations
Inserting or removing at the head of a linked list is O(1):
Only pointer updates are needed, regardless of list length. The key is maintaining direct references to the positions where operations occur.
Getting collection size
Well-designed collections maintain their size, making length queries O(1):
The data structure updates the count during insertions and deletions rather than counting elements on demand.
O(1) does not mean fast
A common misconception is that O(1) means "fast" or "instant." It means the time is constant, not small.
Consider an O(1) operation that takes 1 second. It is still O(1) because it takes 1 second whether you have 1 element or 1 billion. Compare this to an O(n) operation that takes 1 millisecond per element. For small inputs, the O(n) operation is faster:
Input Size | O(1) at 1 second | O(n) at 1ms per element |
|---|---|---|
10 | 1 second | 10 milliseconds |
100 | 1 second | 100 milliseconds |
1,000 | 1 second | 1 second |
10,000 | 1 second | 10 seconds |
The O(1) operation becomes faster than O(n) only when n exceeds 1,000.
In practice, O(1) operations usually are fast because they involve simple memory lookups or arithmetic. But the guarantee is about scaling, not absolute speed.
Constant factors in O(1) operations
Different O(1) operations have different constant factors:
Operation | Typical time |
|---|---|
Integer addition | ~1 nanosecond |
Floating-point division | ~10 nanoseconds |
Array access (cached) | ~1 nanosecond |
Array access (uncached) | ~100 nanoseconds |
Hash table lookup | ~50-200 nanoseconds |
Disk block read | ~10 milliseconds |
All are O(1), but their actual speeds differ by factors of millions.
Amortized O(1)
Some operations are O(1) amortized, meaning they are usually O(1) but occasionally more expensive. The expensive operations are rare enough that they average out to O(1) over many calls.
Dynamic array growth
Most push operations are O(1), but when the array's capacity is exceeded, it must allocate new memory and copy all elements, which is O(n). If the array doubles in size each time, this happens only O(log n) times for n insertions, averaging to O(1) per insertion.
Here is the math: If we start with capacity 1 and double each time, insertions trigger copying at sizes 1, 2, 4, 8, 16, ..., up to n. Total copying work: 1 + 2 + 4 + 8 + ... + n ≤ 2n. For n insertions, total work is at most 2n, which is O(1) per insertion on average.
Hash table resizing
Hash tables occasionally rehash all elements when load factor gets too high:
Like dynamic arrays, this is O(n) occasionally but O(1) amortized per operation.
Understanding amortized bounds
The banker's method helps understand amortized complexity. Imagine each cheap operation "deposits" extra time into a bank account, and expensive operations "withdraw" from this account.
For dynamic arrays:
Each push deposits 3 time units
When resizing triggers, the accumulated deposits pay for copying
As long as deposits exceed withdrawals, the amortized cost remains O(1)
When O(1) is not really O(1)
Some operations that appear to be O(1) have hidden costs:
Large constant factors
An "O(1)" operation might do significant work that does not depend on n but is still slow:
If the object always has the same structure, this is technically O(1). But if the number of fields varies with some input parameter, it becomes O(fields).
Hidden input dependency
Some operations depend on input characteristics other than the primary n:
Be careful to identify all the variables that affect running time, not just the obvious one.
Memory hierarchy effects
CPU cache behavior can make O(1) operations slower when data is not in cache:
Accessing memory in random order can be 100× slower than sequential access, even though both are O(1).
Virtual memory and paging
When data exceeds physical RAM, the operating system uses disk as virtual memory. Accessing paged-out data causes page faults:
A page fault can take 10 million times longer than a cached memory access, even though both are O(1).
Recognizing O(1) patterns
Operations are typically O(1) when they:
Access a known memory location directly (array indexing, pointer dereferencing)
Perform a fixed number of steps regardless of input
Use hash functions to locate data
Modify only the ends of a data structure (stack, queue with tail pointer)
Read or update a maintained property (length, size, sum)
Operations are NOT O(1) when they:
Must examine each element (searching, summing)
Shift elements (array insertion in middle)
Depend on the value's magnitude (printing a number, string operations)
Traverse a structure to find a position (linked list by index)
Require comparison of variable-length data (string equality)
O(1) space complexity
Some algorithms use O(1) space, meaning they require only a fixed amount of extra memory regardless of input size:
Swapping without extra array
Running calculations
O(1) space algorithms are valuable when memory is constrained or when you want to modify data in place.
O(1) in algorithm design
When designing algorithms, aim for O(1) operations in hot paths, code that runs frequently. Common strategies:
Precomputation
Calculate values once and store them for O(1) lookup:
This trades O(n) space for O(1) query time.
Direct addressing
Use arrays when keys are small integers:
Direct addressing is faster than hash tables when keys are dense integers.
Maintaining state
Keep track of information incrementally rather than recalculating:
Each addition is O(1), and all statistics are available in O(1).
Trading accuracy for speed
Some O(1) algorithms provide approximate answers:
Bloom filters provide O(1) membership testing with some probability of false positives.
Common pitfalls
Assuming library operations are O(1)
Some seemingly simple operations are not O(1):
Assuming hash operations are always O(1)
Hash table operations are O(1) average case but can degrade:
In the worst case (all keys collide), hash table operations become O(n).
Ignoring the cost of key comparison
Even O(1) hash lookups require comparing keys:
For long keys, comparison overhead can dominate.
Summary
O(1) means execution time is constant, independent of input size.
Common O(1) operations: array access, hash table operations, stack push/pop, arithmetic.
O(1) does not mean "fast," just that time does not grow with input.
Amortized O(1) operations are usually O(1) but occasionally more expensive.
Watch for hidden costs: large constants, input-dependent work, cache effects.
O(1) space means using only a fixed amount of extra memory.
Design for O(1) in frequently executed code paths using precomputation, direct addressing, and incremental state.
Many seemingly O(1) operations (array shift, string comparison) are actually O(n).
The constant in O(1) can vary by factors of millions between different operations.