Amortized analysis is a powerful technique used to analyze the time complexity of algorithms, especially when an occasional expensive operation is offset by many cheap ones. Let’s explore this concept step-by-step with a concrete example.
Understanding the Problem
Suppose we have a fully filled array of
elements, and we want to insert one more element. The typical algorithm to do this involves:
- Creating a new array of size
. - Copying
elements from the old array into the new one. - Inserting the new element.
The complexity of copying
elements is
, and inserting a new element is
. Therefore, the total time complexity is:
![]()
Can We Improve It?
The question arises: Can we design a data structure where inserting elements is always
and retrieving any element is also
?
The answer is yes, and one such structure is Java’s ArrayList. It offers:
- Infinite logical capacity.
- Element insertion complexity:
(amortized). - Single element retrieval complexity:
.
But how is this achieved?
Internal Working of ArrayList
Internally, ArrayList uses an array. When the internal array is full, it creates a new array of twice the size. Then:
- Copies all existing elements into the new array.
- Inserts the new element.
Example: Inserting 7 into [5, 3, 1, 9] triggers resizing:
- New array is created with size
(if current size is
). - Old elements are copied:
time. - New element is inserted:
.
We call such insertions boundary insertions—those that trigger array resizing.
So:
- Non-boundary insertions:

- Boundary insertions:

Need for Amortized Analysis
Using worst-case time complexity (
) for each insertion would be too pessimistic because most insertions are cheap. Instead, we use amortized analysis, which averages the cost over a sequence of operations.
Amortized analysis answers:
What is the average cost of each operation in a sequence of
operations?
Aggregation Method (Most Common Approach)
Steps:
- Compute total time for a sequence of
insertions (including cheap and expensive ones). - Use the formula:
![]()
Let’s understand this with an example: inserting elements from
to
into an initially empty ArrayList.
Visualizing Resizing and Copy Operations
Let’s build a table:
| Insertion Index | Boundary Element? | Copy Operations |
| 0 | Yes | 0 |
| 1 | Yes | |
| 2 | Yes | |
| 4 | Yes | |
| 8 | Yes | |
| 16 | Yes |
Each time the size reaches a power of 2, a resize (boundary insertion) is triggered. So:
- Total copies due to resizing:

- Number of insertions:
(from 0 to 16)
Using the formula for geometric series:
![]()
Since
, then
. So total copy operations =
.
Also, there are
insertions, each with
insertion cost, which totals to
.
Total Complexity of n Insertions
Total time:
![]()
Amortized Cost Per Insertion
Using the formula:
![]()
Summary and Final Notes
- Worst-case cost of a single insertion:

- Best-case cost of a single insertion:

- Amortized cost per insertion (over a sequence):

That’s why ArrayList can offer fast average-time insertions, even though some insertions take linear time.
