07. Lists, Arrays, and Sequential Data
A practical guide to storing, accessing, traversing, searching, and modifying ordered collections while avoiding common indexing and boundary errors.
Collections, Elements, and Positions
A collection lets a program keep related values under one name instead of creating a separate variable for every value. A list is an ordered sequence of elements, and an is another common name for a similar structure.
For example, a collection of scores can contain the values , , and . The collection has one identity, while each score remains a separate element. This organization reduces repeated code and makes it easier to process many values with the same algorithm.
Positions and boundaries
An identifies an element's position. In zero-based indexing, the first element has , the second has , and so on. For a collection with length , the usual valid indexes are
The length counts elements; it is not itself usually a valid . Thus, a collection of length has last valid . Attempting to access is an out-of-range access in many languages.
Some languages provide negative indexes. In Python, for example, refers to the final element.
Takeaway: Think of length as a count and an as a position. In a zero-based collection, the last position is one less than the length.
Creating and Modifying Collections
A collection may begin with values or may begin empty. An existing element can be replaced by assigning a new value to its . Replacing an element normally changes the value at that position without changing the collection's length.
Common collection operations include:
Append: add an element at the end.
Insert: add an element at a specified position.
Remove: delete an element by value or position.
Clear: delete all elements.
The operation names and exact behavior vary by language. For example, Python provides operations including append, insert, remove, pop, and clear.
Reading versus changing
When a program only reads values, a value-based loop is often sufficient. When it must assign a replacement back into the collection, it generally needs an . Updating every negative value to zero, for example, requires identifying each element's position before assigning the replacement.
Changing an existing collection is . Creating a separate result collection preserves the original data. This distinction matters when multiple variables refer to the same collection: an in-place change may be visible through every reference to that collection.
Takeaway: Choose operations deliberately: read values when calculating, use indexes when assigning, and decide whether the result should mutate the original collection or be stored separately.
Traversing and Accumulating
means visiting collection elements systematically. A value-based is appropriate when the algorithm needs each value but not its position. An -based is appropriate when the algorithm needs positions, must update elements, or must compare neighboring elements.
A typical range for a collection of length begins at and ends at . Stopping at would attempt one access beyond the final element.
Building results during
An begins with an initial value and is updated as elements are processed. To total the values , , and , initialize the total to , then add each element:
After processing , the total is .
After processing , the total is .
After processing , the total is .
The same pattern can count elements satisfying a condition, calculate an average, or build a result collection. A count of passing scores, for instance, starts at and increases by whenever a score is at least .
Takeaway: A supplies the repeated processing step, while an preserves the result built so far.
Selecting, Transforming, and Updating
Many collection algorithms can be understood as one of three related patterns.
Selecting elements
keeps only elements that satisfy a condition. To create a collection of passing scores, examine each score and append it to the result when the score is at least . The original scores remain available if the passing results are stored separately.
Producing corresponding values
creates one output value for each input value. If the input values are , , and , multiplying each by produces , , and . A new collection is useful when the original values must be preserved.
Finding an extreme value
To find the largest value in a nonempty collection, initialize the candidate maximum with the first element. Compare the remaining elements with that candidate and replace it when a larger value is found. Initializing the maximum to is unsafe if all valid inputs might be negative.
Updating in place
An -based can replace each element directly. For example, multiplying every element by changes the original collection rather than creating a separate doubled collection.
Takeaway: Selecting keeps some elements, transforming creates corresponding results, and in-place updating changes existing elements. The intended result determines which pattern to use.
Searching and Combining Operations
A examines elements in order until it finds a target or reaches the end. A useful search procedure returns the matching when the target is found and returns when it is absent.
For the collection , searching for returns . Searching for returns . The nonnegative result communicates success, while communicates failure.
A has worst-case running time , where is the collection's length, because it may inspect every element. A sorted collection may support a binary search with fewer comparisons, but binary search is a different strategy and depends on maintaining sorted data.
Search and update can be combined. For example, an algorithm can traverse a collection and replace every negative value with . Because the algorithm assigns values back into the original positions, it needs indexes rather than only copied element values.
Takeaway: A search needs a clear result convention, and an update needs a way to identify the position being changed.
Nested Data and Reliable Boundaries
A collection can contain other collections. This creates a row-and-column structure suitable for tables, grids, game boards, and spreadsheets. For example, a two-row table can contain the rows and .
The first selects a row, and the second selects a column. In this example, row and column identify the value . Processing every cell requires a loop for the rows and an inner loop for the elements of each row.
If a rectangular table has rows and columns, visiting every cell requires approximately element visits. Each loop must respect the valid bounds of the collection it traverses.
Boundary and correctness checks
Test collection algorithms with:
An empty collection.
A one-element collection.
A target at the first position.
A target at the last position.
A target that does not occur.
Duplicate values.
Negative values when they are allowed.
An attempted equal to the collection length.
Frequent errors include starting at and skipping the first element, accessing instead of stopping at , initializing an extreme-value search with an unsuitable constant, changing collection length during , and unintentionally mutating shared data.
Takeaway: Boundary cases reveal off-by-one errors and unsafe assumptions that ordinary examples may hide.