Array Operations
Insert, delete, search, traverse, update — every fundamental array operation with step-by-step walkthroughs and complexity analysis.
What Is It?
Knowing how an array is stored in memory is step one. Step two is understanding what you can do with it.
Arrays support six core operations: access (get an element by index), search (find an element by value), insert (add a new element), delete (remove an element), traverse (visit every element), and update (change a value at an index).
Some of these are instant. Others slow down as the array gets bigger. Knowing the difference is what lets you write fast programs.
Analogy
The Bookshelf
Imagine a bookshelf with 10 numbered slots, packed tightly left to right with no gaps.
- Access: "Give me the book in slot 5." Instant — you walk straight to it. O(1) — always fast, no matter the size.
- Search: "Do you have Moby Dick?" You scan from slot 0 through every book until you find it or reach the end. O(n) — takes longer as the shelf gets bigger.
- Insert at end: Put a new book in the first empty slot. O(1) — always fast.
- Insert in middle: "Put this book in slot 3." You must slide every book from slot 3 onward one spot to the right. O(n) — takes longer as the shelf gets bigger.
- Delete from middle: Remove the book at slot 3. Now there's a gap. Slide every book after it one spot left. O(n) — takes longer as the shelf gets bigger.
- Update: Replace the book in slot 5. Walk there, swap. O(1) — always fast.
How It Works
ACCESS — O(1), always fast
Accessing an element by index uses the address formula from the previous lesson. One calculation, done.
function access(array, index):if index < 0 or index >= array.length:error "Index out of bounds"return array[index]
Trace with array = [10, 20, 30, 40, 50], accessing index 2:
SEARCH — O(n), takes longer as the array gets bigger
To find an element by value (not index), you have to check each element one by one. You don't know where it is.
function linearSearch(array, target):for i = 0 to array.length - 1:if array[i] == target:return i // found it at index ireturn -1 // not in the array
Trace with array = [10, 20, 30, 40, 50], searching for 30:
In the worst case, the target is at the very end (or not there at all). You check every element — that's n checks for n elements.
INSERT — O(1) at the end, O(n) anywhere else
Adding to the end (if there's room) is simple:
function insertAtEnd(array, value, currentSize):if currentSize >= array.capacity:error "Array is full"array[currentSize] = valuecurrentSize = currentSize + 1
Inserting in the middle is expensive. You have to shift everything after the insertion point one slot to the right to make room:
function insertAt(array, index, value, currentSize):if currentSize >= array.capacity:error "Array is full"if index < 0 or index > currentSize:error "Invalid index"// Shift elements right, starting from the endfor i = currentSize - 1 down to index:array[i + 1] = array[i]array[index] = valuecurrentSize = currentSize + 1
Step-by-step trace — inserting 25 at index 2 in [10, 20, 30, 40, 50]:
We shifted 3 elements. Inserting at the very beginning shifts ALL elements — that's the worst case.
Shift every element one slot to the right:
Write 5 at index 0:
DELETE — O(1) at the end, O(n) anywhere else
Removing the last element is trivial:
function deleteAtEnd(array, currentSize):if currentSize == 0:error "Array is empty"currentSize = currentSize - 1// The value is still in memory, but we treat it as outside the valid range
Deleting from the middle requires shifting elements left to fill the gap:
function deleteAt(array, index, currentSize):if index < 0 or index >= currentSize:error "Invalid index"// Shift elements left, starting from the deletion pointfor i = index to currentSize - 2:array[i] = array[i + 1]currentSize = currentSize - 1
Step-by-step trace — deleting element at index 1 from [10, 20, 30, 40, 50]:
TRAVERSE — O(n), takes longer as the array gets bigger
Visiting every element. Forward or backward:
function traverseForward(array, size):for i = 0 to size - 1:visit(array[i])function traverseBackward(array, size):for i = size - 1 down to 0:visit(array[i])
You visit each element once, so it always takes n steps for n elements.
UPDATE — O(1), always fast
Changing a value at a known index:
function update(array, index, newValue):if index < 0 or index >= array.length:error "Index out of bounds"array[index] = newValue
Same as access — you jump directly to the position and overwrite it.
Operation Summary
Examples
Example 1: Counting shifts
Array of 1000 elements. Insert at index 0 (the beginning):
- Shift all 1000 elements one slot right
- Write the new value
- Total: 1001 steps
Insert at index 999 (near the end):
- Shift only 1 element
- Write the new value
- Total: 2 steps
Both are technically O(n) — but in practice, inserting near the end is much faster.
Example 2: Search then delete
A common pattern: "find and remove the value 30 from [10, 20, 30, 40, 50]"
// Step 1: Search for 30index = linearSearch(array, 30) // checks until it finds 30 at index 2// Step 2: Delete at index 2deleteAt(array, 2) // shifts 40 and 50 left// Result: [10, 20, 40, 50]
Common Mistakes
- Shifting in the wrong direction during insert. When inserting, always shift from the end toward the insertion point. If you start from the insertion point and go right, you'll overwrite elements before you've copied them.
- Forgetting to update the size. After every insert or delete, the number of valid elements changes. If you don't track this, you'll either read garbage values or skip valid ones.
- Assuming insert is always O(1). Adding to the end is O(1). Adding anywhere else is O(n) because of shifting. Don't forget this.
- Not handling edge cases. What if the array is empty and you delete? What if it's full and you insert? What if you insert at position 0? Test these cases explicitly.
Best Practices
- If you mostly read by index, arrays are great — O(1) access is as fast as it gets.
- If you frequently add or remove elements from the middle, arrays are a poor fit. A different data structure (like a linked list) avoids the shifting cost.
- When you need to delete an element and you don't care about order, swap it with the last element and delete from the end. This turns an O(n) delete into O(1).
- Always track both the array's capacity (total slots) and its current size (elements stored). They are not the same thing.