cs_survival_kit.data_structures.dynamic_array ¶
A dynamic array with pluggable growth policies.
Provides DynamicArray, a resizable array backed by fixed-capacity storage,
and a set of growth policies that decide how much capacity to add each time
the array resizes. The growth policy determines the amortized cost of
append: geometric policies give amortized O(1) appends, while additive
policies give amortized O(n) appends.
GrowthPolicy ¶
Maps the current capacity to the new capacity after an array resize.
A valid policy must return a value strictly greater than its input.
DynamicArray.append raises ValueError if a policy does not.
DynamicArray ¶
Bases: AbstractList[T]
A resizable array backed by fixed-capacity storage.
Elements are stored in a fixed-size backing list of capacity slots,
where only the first len(self) slots are in use. When an insert finds
no free slot, the array first resizes: it allocates a larger backing list,
sized by the growth policy, and copies every element into it. The backing
storage never shrinks, so popping elements leaves capacity unchanged.
Elements sit at evenly spaced positions, so reaching any index is one
arithmetic step. The price is paid on insertion and removal anywhere but
the end: every later element has to shift by one slot to keep the block
contiguous. The inherited append and pop_back work at the end and
shift nothing; the inherited prepend and pop_front work at index 0 and
shift everything.
Unlike list, indexing accepts only non-negative indices in the range
0 <= index < len(self). Negative indices and slices are not supported.
Complexity
| Operation | Time | Space |
|---|---|---|
a[i], a[i] = x |
O(1) | O(1) |
insert |
O(n) amortized | O(1) amortized, O(n) worst |
pop |
O(n) | O(1) |
append |
O(1) amortized, O(n) worst | O(1) amortized, O(n) worst |
pop_back |
O(1) | O(1) |
prepend |
O(n) | O(1) amortized, O(n) worst |
pop_front |
O(n) | O(1) |
remove |
O(n) | O(1) |
len(a) |
O(1) | O(1) |
item in a |
O(n) | O(1) |
| iteration | O(n) | O(1) |
reversed(a) |
O(n) | O(1) |
reverse |
O(n) | O(1) |
| resize | O(n) | O(n) |
insert at index i shifts n - i elements, so it is O(1) at the
end and O(n) at the front; pop likewise. reverse is the inherited
default, swapping inward from both ends, which an array can do
because both ends are O(1) away. reversed(a) overrides the inherited
default, which buffers a forward pass in O(n) space, with a backward
walk by index: a[i] is O(1), so the buffer would buy nothing. The
amortized bounds
assume a geometric growth policy such as the default doubling.
With an additive policy, append is amortized O(n). Total storage
is O(capacity). For an array built by appends alone, doubling keeps
capacity below 2n once the array has grown past its initial capacity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
items
|
Iterable[T]
|
Elements to add to the new array, in order. Defaults to empty. Each is appended, so the array grows by the policy as it fills. |
()
|
capacity
|
int
|
The number of slots to allocate up front. Must be at least 1. |
4
|
growth
|
GrowthPolicy
|
The growth policy that computes the new capacity on each
resize. Must return a value greater than its input. Defaults to
|
doubling
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> a = DynamicArray[int](capacity=2)
>>> a.append(1)
>>> a.append(2)
>>> len(a), a.capacity
(2, 2)
>>> a.append(3) # no free slot, so the array doubles first
>>> len(a), a.capacity
(3, 4)
>>> a
DynamicArray([1, 2, 3])
>>> a[0] = 10
>>> a.pop_back()
3
>>> a.insert(1, 15)
>>> list(a)
[10, 15, 2]
- Data Structures Dynamic Array
- Reference (cs-survival-kit 0.8.0) cs_survival_kit
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
capacity
property
¶
The number of allocated slots, used or not.
Always at least len(self). The two are equal when the array is full,
and the next insert will trigger a resize.
Complexity
- Time: O(1)
- Space: O(1)
__len__ ¶
Return the number of elements in the array.
Returns:
| Type | Description |
|---|---|
int
|
The number of elements in the array. |
Complexity
- Time: O(1)
- Space: O(1)
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
__getitem__ ¶
Return the element at index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
index
|
int
|
The position of the element. Must satisfy
|
required |
Returns:
| Type | Description |
|---|---|
T
|
The element at |
Raises:
| Type | Description |
|---|---|
IndexError
|
If |
Complexity
- Time: O(1)
- Space: O(1)
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
__setitem__ ¶
Replace the element at index with item.
Only overwrites an existing element. Use insert or append to add
one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
index
|
int
|
The position of the element to replace. Must satisfy
|
required |
item
|
T
|
The new element. |
required |
Raises:
| Type | Description |
|---|---|
IndexError
|
If |
Complexity
- Time: O(1)
- Space: O(1)
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
__iter__ ¶
Iterate over the elements from index 0 to len(self) - 1.
Unused slots are never visited, so None in a is True only if
None was actually stored.
Yields:
| Type | Description |
|---|---|
T
|
Each element, in index order. |
Complexity
- Time: O(n) to exhaust the iterator
- Space: O(1)
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
__reversed__ ¶
Iterate over the elements from index len(self) - 1 down to 0.
Overrides the inherited default, which buffers one forward pass in O(n) auxiliary space so that it stays linear on structures that cannot index cheaply. An array indexes in O(1), so it walks backward by index instead and needs no buffer.
Yields:
| Type | Description |
|---|---|
T
|
Each element, starting with the one at index |
Complexity
- Time: O(n) to exhaust the iterator
- Space: O(1)
Examples:
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
insert ¶
Add item at index, shifting every later element one slot right.
If the array is full, it first resizes to the capacity returned by the
growth policy, copying every element, and only then makes room.
Resizing only when there is no free slot means every allocated slot
gets used before the array grows. The shift then runs from the last
element down to index, so no element is overwritten before it has
been moved.
Inserting at len(self) shifts nothing, which is why the inherited
append is O(1) amortized. Inserting at 0 shifts every element, which
is why the inherited prepend is O(n).
The growth policy's result is checked before anything changes. If it
is not greater than the current capacity, ValueError is raised and
the array is left exactly as it was.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
index
|
int
|
The position the new element will occupy. Must satisfy
|
required |
item
|
T
|
The element to add. |
required |
Raises:
| Type | Description |
|---|---|
IndexError
|
If |
ValueError
|
If the array is full and the growth policy returns a capacity that is not greater than the current capacity. |
Complexity
- Time: O(n - index) to shift, plus O(n) for the insert that triggers a resize; O(1) amortized at the end with a geometric growth policy
- Space: O(1) amortized; O(n) for the insert that triggers a resize
Examples:
>>> a = DynamicArray[str](capacity=2)
>>> a.append("x")
>>> a.append("z") # fills the last free slot; no resize yet
>>> a.capacity
2
>>> a.insert(1, "y") # no free slot, so the array grows first
>>> a, a.capacity
(DynamicArray(['x', 'y', 'z']), 4)
A growth policy that doesn't grow is rejected:
>>> stuck = DynamicArray[int](capacity=1, growth=lambda c: c)
>>> stuck.append(1)
>>> stuck.append(2)
Traceback (most recent call last):
...
ValueError: growth policy must increase capacity (1 -> 1)
>>> list(stuck), stuck.capacity
([1], 1)
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
pop ¶
Remove and return the element at index, shifting later ones left.
The shift runs from index + 1 up to the last element, so each slot
is overwritten only after its element has moved. The slot freed at
the end is cleared, so the array no longer holds a reference to the
element. Capacity is unchanged.
Popping at len(self) - 1 shifts nothing, which is why the inherited
pop_back is O(1). Popping at 0 shifts every remaining element, which
is why the inherited pop_front is O(n).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
index
|
int
|
The position of the element to remove. Must satisfy
|
required |
Returns:
| Type | Description |
|---|---|
T
|
The element that was at |
Raises:
| Type | Description |
|---|---|
IndexError
|
If |
Complexity
- Time: O(n - index) to shift; O(1) at the end
- Space: O(1)
Examples:
>>> a = DynamicArray[int]()
>>> for item in (1, 2, 3):
... a.append(item)
>>> a.pop(0)
1
>>> a
DynamicArray([2, 3])
>>> a.pop(2)
Traceback (most recent call last):
...
IndexError: index out of range
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
doubling ¶
Double the capacity on each resize.
The default growth policy for DynamicArray. Equivalent to
geometric(2.0).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
curr_capacity
|
int
|
The current capacity of the array. |
required |
Returns:
| Type | Description |
|---|---|
int
|
Twice the current capacity. |
Examples:
- Data Structures Dynamic Array Growth policies
- Reference (cs-survival-kit 0.8.0) cs_survival_kit
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
geometric ¶
Build a growth policy that multiplies the capacity by a constant factor.
Any factor greater than 1 gives amortized O(1) appends. The factor trades
memory for copying: a resize copies every element, so a larger factor means
fewer resizes, but right after a resize the capacity is about factor times
the number of elements. CPython's list over-allocates by roughly ⅛,
choosing low memory overhead over fewer resizes.
Because the new capacity is truncated to an integer, a small factor applied to a small capacity could round back down to the current capacity. The returned policy always grows the capacity by at least 1 to prevent that.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
factor
|
float
|
The multiplier applied to the current capacity. Must be finite and greater than 1. |
required |
Returns:
| Type | Description |
|---|---|
GrowthPolicy
|
A growth policy that returns |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
- Data Structures Dynamic Array Growth policies
Source code in cs_survival_kit/data_structures/dynamic_array.py
·
View on GitHub
additive ¶
Build a growth policy that adds a fixed number of slots on each resize.
This policy is a counterexample to geometric growth. A resize happens
every step appends and copies every element, so n appends perform about
n² / (2 · step) copies in total. That is amortized O(n) per append, and
O(n²) to build an array of n elements. In exchange, a resize never leaves
more than step unused slots.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
step
|
int
|
The number of slots to add on each resize. Must be at least 1. |
required |
Returns:
| Type | Description |
|---|---|
GrowthPolicy
|
A growth policy that returns |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
- Data Structures Dynamic Array Growth policies