Arrays and Strings
5 min read
Dsa Index
Dsa Index
Arrays and Strings
1. What It Is and Why It Exists
An array is a contiguous block of memory holding elements of the same type, addressed by an integer index. Because the elements are contiguous and equal-sized, the address of element i is computed in one arithmetic step (base + i * size), which is why random access is O(1). That single property — instant indexed access — is what makes arrays the default container for almost every algorithm: , , prefix sums, sorting, and all rely on cheap positional access. Without it you would be back to linked structures where reaching element i costs O(i).
A string is logically an array of characters with one twist that dominates interview answers: in Python, strings are immutable. Any "modification" (s + c, s.replace(...)) builds a new string in O(n). The classic trap is concatenating in a loop, which is O(n^2); the fix is to collect characters in a list and "".join(...) once at the end. Python's list is a dynamic array — it resizes automatically with amortized-O(1) append — so "array" in a Python interview means list.
A backend service builds a response string by repeatedly concatenating individual characters inside a loop: result = ""; for char in data: result += char. For an input of 10,000 characters, what is the time complexity of this approach, and what is the recommended fix in Python?
2. Operations and Complexity
| Operation | Average | Worst | Notes |
|---|---|---|---|
Index read/write a[i] | O(1) | O(1) | The defining strength |
Append a.append(x) | O(1) amortized | O(n) | Resize copy amortizes away (doubling) |
Pop end a.pop() | O(1) | O(1) | — |
Insert/delete at index i | O(n) | O(n) | Shifts all elements after i |
pop(0) / insert(0, x) | O(n) | O(n) | Front ops shift everything — use deque |
Search (unsorted) x in a | O(n) | O(n) | Linear scan |
| Search (sorted) | O(log n) | O(log n) | Binary search |
Slice a[i:j] | O(j-i) | O(n) | Copies the slice |
len(a) | O(1) | O(1) | Stored, not counted |
Space is O(n). The operations interviewers actually probe: insert/delete in the middle is O(n) (the shift) and front operations are O(n) — knowing to switch to collections.deque for -like access is a frequent signal. For strings, the probe is "you concatenated in a loop — what's the complexity?" (O(n^2)).
A backend service processes a high-throughput message queue by repeatedly calling list.pop(0) to dequeue messages from the front of a Python list. As the list grows to millions of entries, this operation becomes a bottleneck. What is the time complexity of pop(0) on a Python list, and what is the recommended fix?
3. Implementation
A fixed-capacity array with O(1) indexing and the O(n) shift made explicit:
class Array: def __init__(self, capacity): self._data = [None] * capacity # contiguous slots self._n = 0 self._cap = capacity def get(self, i): # O(1): direct address arithmetic if not 0 <= i < self._n: raise IndexError return self._data[i] def set(self, i, x): # O(1) if not 0 <= i < self._n: raise IndexError self._data[i] = x def insert(self, i, x): # O(n): shift right to make a hole if self._n == self._cap: raise OverflowError for j in range(self._n, i, -1): # this shift IS the O(n) cost self._data[j] = self._data[j - 1] self._data[i] = x self._n += 1 def delete(self, i): # O(n): shift left to fill the gap for j in range(i, self._n - 1): self._data[j] = self._data[j + 1] self._n -= 1
In a real interview, use Python's built-in list — it is already a dynamic array with amortized-O(1) append. For string building, use a list + "".join(...), never += in a loop. Reach for collections.deque the moment you need O(1) operations at the front.
A backend service maintains an ordered list of active user sessions stored in an array. A new session must be inserted at position 0 (the front) so the list stays sorted by arrival time. The array currently holds 10,000 sessions. What is the time complexity of this insertion, and why?
4. When to Reach for It
- You need indexed/random access or the input is already a sequence you scan by position.
- The problem screams , , or — all require
O(1)positional access. - You need a sorted sequence for (
O(log n)lookups). - You are building a string incrementally → array of chars +
join. - You need a (append/pop end are
O(1)) — alistis the natural .
A backend engineer needs to build a feature that finds the maximum sum of any contiguous subarray of length k within a stream of API response times. Which data structure choice best supports this requirement, and why?
5. Common Pitfalls
- String concatenation in a loop:
s += cfornchars isO(n^2)(immutable rebuild each time). Fix:"".join(list_of_chars). list.pop(0)/list.insert(0, x)in a loop: each isO(n), so the loop isO(n^2). Fix:collections.deque(O(1)both ends), or reverse the logic to work from the back.- Mutating a list while iterating it: skips elements or raises; iterate a copy or build a new list.
[[0]*n]*maliasing: all rows are the same list object; mutating one mutates all. Use[[0]*n for _ in range(m)].- Off-by-one on bounds:
range(n)vsrange(n-1), inclusive vs exclusive slice ends — the most common array bug interviewers watch for. - Forgetting slices copy:
a[1:]inside a loop is a hiddenO(n)per call →O(n^2).
A backend developer writes the following Python code to build a response string from a list of 10,000 log tokens:
What is the time complexity of this operation, and what is the recommended fix?result = "" for token in log_tokens: result += token
6. Interview Cheat Sheet
- "Array gives
O(1)indexed access because elements are contiguous; the cost isO(n)insert/delete in the middle due to shifting." - "Python
listis a dynamic array:append/popat the end are amortizedO(1);insert(0)/pop(0)areO(n)— I'd usedequefor front operations." - "Python strings are immutable, so I build with a list and
''.jointo avoid anO(n^2)concatenation." - "Allocating extra space is
O(n); in-place rearrangement keeps itO(1)auxiliary."
Follow-ups:
- "Array vs ?" — Array:
O(1)access,O(n)middle insert, cache-friendly. :O(n)access,O(1)insert/delete at a known node. Interviews almost always want an array unless the problem needsO(1)splicing. - "Why is your string building
O(n^2)?" — Immutable strings: each+=copies the whole prefix. Use a list and join →O(n). - "How do you delete from the middle in
O(1)?" — You can't preserve order inO(1); if order doesn't matter, swap the target with the last element andpop()(O(1)).
Glossary History
Click dotted jargon to save explanations here.
Glossary History
Click dotted jargon to save explanations here.