Python for DSA — Lists
3. Lists
💡 Why this matters for DSA Lists are the backbone of DSA in Python — stacks, queues, dynamic arrays, and matrices are all built on lists.
3.1 Creating, Indexing & Slicing
arr = [1, 2, 3, 4, 5]
arr[0] # 1
arr[-1] # 5
arr[1:4] # [2, 3, 4]
arr[::-1] # [5, 4, 3, 2, 1] (reverse)
arr[::2] # [1, 3, 5] (every other)
⚠️ Warning: Slicing a list creates a new copy (O(n)). Use it intentionally.
Common initialization patterns
# Fixed-size list
zeros = [0] * 10 # [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# 2D grid (matrix)
grid = [[0] * 3 for _ in range(3)] # [[0,0,0],[0,0,0],[0,0,0]]
# ❌ Wrong way — creates shared references!
bad = [[0] * 3] * 3 # Each row is the SAME list object
🚨 Danger — The
*trap for 2D lists:pythonbad = [[0] * 3] * 3 bad[0][0] = 1 print(bad) # [[1,0,0],[1,0,0],[1,0,0]] — all rows changed!Always use list comprehension for 2D:
[[0]*3 for _ in range(3)]
3.2 Essential Methods
arr = [1, 2, 3]
arr.append(4) # [1, 2, 3, 4] O(1)
arr.pop() # returns 4, arr = [1, 2, 3] O(1)
arr.pop(0) # returns 1, arr = [2, 3] O(n)
arr.insert(0, 0) # [0, 2, 3] O(n)
arr.remove(2) # [0, 3] O(n) (removes first match)
arr.sort() # in-place sort O(n log n)
arr.reverse() # in-place reverse O(n)
arr.copy() # shallow copy O(n)
len(arr) # 2 O(1)
✅ Tip — Using list as a stack (LIFO):
pythonstack = [] stack.append(1) # push stack.append(2) stack.pop() # pop → 2
✅ Tip — Using list as a queue (FIFO) — ❌ not recommended:
pythonqueue = [] queue.append(1) # enqueue queue.pop(0) # dequeue → O(n)! Use collections.deque instead
3.3 Shallow vs Deep Copy
This is a common point of confusion. Understanding it saves hours of debugging.
Shallow Copy (.copy(), [:], list())
Copies the outer list, but inner lists are still shared references.
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0][0] = 99
print(original) # [[99, 2], [3, 4]] ❌ Changed!
print(shallow) # [[99, 2], [3, 4]]
# Why? Because shallow[0] and original[0] point to the SAME list object.
# Visually:
original ──→ [ list_A , list_B ]
↑ ↑
shallow ──→ [ list_A , list_B ]
Deep Copy (copy.deepcopy())
Recursively copies everything — no shared references.
import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0][0] = 99
print(original) # [[1, 2], [3, 4]] ✅ Unchanged!
print(deep) # [[99, 2], [3, 4]]
# Visually:
original ──→ [ list_A , list_B ]
deep ──→ [ list_C , list_D ] (brand new inner lists)
When to use what
| Scenario | Use |
|---|---|
| 1D list of primitives | arr.copy() or arr[:] |
| 2D list / nested lists | copy.deepcopy(arr) |
| You want to share inner structures | Shallow copy intentionally |
✅ Tip: For DSA, you'll mostly deal with 1D lists — shallow copy is usually enough. Deep copy becomes relevant when building complex state (backtracking, memoization).
# Basic
squares = [x**2 for x in range(5)] # [0, 1, 4, 9, 16]
# With condition
evens = [x for x in range(10) if x % 2 == 0] # [0, 2, 4, 6, 8]
# Nested loop (flattening)
flat = [x for row in matrix for x in row]
# With transformation
negatives = [-x for x in [1, -2, 3, -4]] # [-1, 2, -3, 4]
✅ Tip — When to use list comprehension vs loop: Use comprehension for transforming one list to another. Use loop for side effects (printing, modifying in place).
3.4 Nested Lists (Matrices)
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
matrix[0][1] # 2 (row 0, col 1)
matrix[1] # [4, 5, 6] (entire row)
# Transpose
transposed = [[row[i] for row in matrix] for i in range(3)]
Practice Drill
# 1. Find the largest element in a list
# 2. Remove duplicates from a list (order preserved)
# 3. Reverse a list in-place (without slicing)
# 4. Given a matrix, print all elements in spiral order (hard)
💡 Click for Solutions
# 1. Largest element
arr = [3, 7, 2, 9, 5]
print(max(arr)) # 9
# 2. Remove duplicates (order preserved)
arr = [1, 2, 2, 3, 1, 4]
seen = set()
unique = []
for x in arr:
if x not in seen:
unique.append(x)
seen.add(x)
print(unique) # [1, 2, 3, 4]
# 3. Reverse in-place
arr = [1, 2, 3, 4, 5]
left, right = 0, len(arr) - 1
while left < right:
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1
print(arr) # [5, 4, 3, 2, 1]
# 4. Spiral matrix
def spiral_order(matrix):
res = []
while matrix:
res += matrix.pop(0)
if matrix and matrix[0]:
for row in matrix:
res.append(row.pop())
if matrix:
res += matrix.pop()[::-1]
if matrix and matrix[0]:
for row in matrix[::-1]:
res.append(row.pop(0))
return res
matrix = [[1,2,3],[4,5,6],[7,8,9]]
print(spiral_order(matrix)) # [1,2,3,6,9,8,7,4,5]
← Strings | Next → Tuples & Sets