A Complete Guide to One of Programming's Most Fundamental Data Structures
Table of Contents
- Introduction to Arrays
- What is an Array?
- Array Operations
- Types of Arrays
- Arrays in Different Languages
- Memory Representation
- Common Array Algorithms
- Advanced Array Concepts
- Array vs Other Data Structures
- Practical Examples
- Common Mistakes and Pitfalls
- Interview Questions
Introduction to Arrays
Arrays are one of the most fundamental and widely used data structures in programming. They provide a way to store and organize collections of data efficiently, forming the foundation for more complex data structures and algorithms.
Why Arrays Matter
- Efficiency: Fast access to elements using indices
- Simplicity: Easy to understand and implement
- Memory Efficiency: Contiguous memory allocation
- Foundation: Building block for other data structures
- Ubiquity: Supported in virtually every programming language
What is an Array?
An array is a collection of elements, each identified by an index or key, stored in contiguous memory locations. All elements in an array are of the same data type.
Simple Visualization
An Array of 5 Integers: Index: 0 1 2 3 4 ┌────┬────┬────┬────┬────┐ Value: │ 10 │ 20 │ 30 │ 40 │ 50 │ └────┴────┴────┴────┴────┘ Memory: 1000 1004 1008 1012 1016 (addresses, assuming 4-byte integers)
Key Characteristics
| Characteristic | Description |
|---|---|
| Fixed Size | Size is typically determined at creation |
| Homogeneous | All elements are the same data type |
| Contiguous | Elements stored in consecutive memory locations |
| Indexed Access | Access any element directly using its index |
| Zero-Based Indexing | First element is at index 0 (in most languages) |
Array Declaration in Different Languages
# Python numbers = [1, 2, 3, 4, 5] names = ["Alice", "Bob", "Charlie"] mixed = [1, "hello", 3.14] # Python allows mixed types
// JavaScript let numbers = [1, 2, 3, 4, 5]; let names = ["Alice", "Bob", "Charlie"]; let mixed = [1, "hello", true];
// Java
int[] numbers = {1, 2, 3, 4, 5};
String[] names = {"Alice", "Bob", "Charlie"};
int[] empty = new int[10]; // Array of 10 zeros
// C
int numbers[] = {1, 2, 3, 4, 5};
char names[][20] = {"Alice", "Bob", "Charlie"};
int empty[10]; // Array of 10 integers (uninitialized)
// C#
int[] numbers = {1, 2, 3, 4, 5};
string[] names = {"Alice", "Bob", "Charlie"};
int[] empty = new int[10];
Accessing Array Elements
# Python numbers = [10, 20, 30, 40, 50] # Access by index first = numbers[0] # 10 second = numbers[1] # 20 last = numbers[-1] # 50 (negative indexing) # Modify elements numbers[2] = 35 # Now [10, 20, 35, 40, 50]
// JavaScript let numbers = [10, 20, 30, 40, 50]; // Access by index let first = numbers[0]; // 10 let second = numbers[1]; // 20 // Modify elements numbers[2] = 35; // Now [10, 20, 35, 40, 50]
// Java
int[] numbers = {10, 20, 30, 40, 50};
// Access by index
int first = numbers[0]; // 10
int second = numbers[1]; // 20
// Modify elements
numbers[2] = 35; // Now [10, 20, 35, 40, 50]
Array Operations
Common Array Operations
| Operation | Description | Time Complexity |
|---|---|---|
| Access | Get element at index | O(1) |
| Search | Find element by value | O(n) |
| Insert | Add element at position | O(n) |
| Delete | Remove element at position | O(n) |
| Update | Change element value | O(1) |
| Traversal | Visit all elements | O(n) |
1. Traversal (Iterating Through Arrays)
# Python - different ways to traverse
numbers = [10, 20, 30, 40, 50]
# Method 1: For-each loop
for num in numbers:
print(num)
# Method 2: Index-based loop
for i in range(len(numbers)):
print(f"Index {i}: {numbers[i]}")
# Method 3: While loop
i = 0
while i < len(numbers):
print(numbers[i])
i += 1
# Method 4: Enumerate (with index and value)
for i, num in enumerate(numbers):
print(f"Position {i}: {num}")
// JavaScript
let numbers = [10, 20, 30, 40, 50];
// Method 1: for...of (values only)
for (let num of numbers) {
console.log(num);
}
// Method 2: forEach
numbers.forEach(num => console.log(num));
// Method 3: Traditional for loop
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}
// Method 4: for...in (indices)
for (let i in numbers) {
console.log(numbers[i]);
}
2. Insertion
# Python - Insertion operations numbers = [10, 20, 30, 40, 50] # Append to end numbers.append(60) # [10, 20, 30, 40, 50, 60] # Insert at specific position numbers.insert(2, 25) # [10, 20, 25, 30, 40, 50, 60] # Extend with another list numbers.extend([70, 80]) # [10, 20, 25, 30, 40, 50, 60, 70, 80]
// JavaScript let numbers = [10, 20, 30, 40, 50]; // Append to end numbers.push(60); // [10, 20, 30, 40, 50, 60] // Insert at specific position numbers.splice(2, 0, 25); // [10, 20, 25, 30, 40, 50, 60] // Insert at beginning numbers.unshift(5); // [5, 10, 20, 25, 30, 40, 50, 60]
// Java - arrays have fixed size, use ArrayList for dynamic import java.util.ArrayList; import java.util.Arrays; ArrayList<Integer> numbers = new ArrayList<>(Arrays.asList(10, 20, 30, 40, 50)); // Append to end numbers.add(60); // [10, 20, 30, 40, 50, 60] // Insert at specific position numbers.add(2, 25); // [10, 20, 25, 30, 40, 50, 60]
3. Deletion
# Python - Deletion operations numbers = [10, 20, 30, 40, 50] # Remove by value numbers.remove(30) # [10, 20, 40, 50] # Remove by index deleted = numbers.pop(1) # deletes 20, returns it # numbers now: [10, 40, 50] # Delete last element last = numbers.pop() # removes 50 # Delete slice del numbers[0:2] # removes first two elements
// JavaScript let numbers = [10, 20, 30, 40, 50]; // Remove last element let last = numbers.pop(); // removes 50 // Remove first element let first = numbers.shift(); // removes 10 // Remove by index let deleted = numbers.splice(1, 1); // removes element at index 1 // Filter (conditional removal) numbers = numbers.filter(num => num !== 30); // remove all 30s
4. Searching
# Python - Searching numbers = [10, 20, 30, 40, 50] # Linear search def linear_search(arr, target): for i, val in enumerate(arr): if val == target: return i return -1 index = linear_search(numbers, 30) # returns 2 # Using built-in methods index = numbers.index(30) # returns 2 exists = 30 in numbers # returns True
// JavaScript let numbers = [10, 20, 30, 40, 50]; // indexOf let index = numbers.indexOf(30); // returns 2 // includes let exists = numbers.includes(30); // returns true // find let found = numbers.find(num => num > 35); // returns 40 // findIndex let foundIndex = numbers.findIndex(num => num > 35); // returns 3
5. Sorting
# Python - Sorting numbers = [50, 20, 40, 10, 30] # Ascending sort numbers.sort() # [10, 20, 30, 40, 50] # Descending sort numbers.sort(reverse=True) # [50, 40, 30, 20, 10] # Custom sort (by length of string) words = ["apple", "banana", "kiwi", "cherry"] words.sort(key=len) # ["kiwi", "apple", "cherry", "banana"] # Sorted (returns new list) sorted_numbers = sorted(numbers)
// JavaScript let numbers = [50, 20, 40, 10, 30]; // Ascending sort numbers.sort((a, b) => a - b); // [10, 20, 30, 40, 50] // Descending sort numbers.sort((a, b) => b - a); // [50, 40, 30, 20, 10] // String sort let words = ["apple", "banana", "kiwi", "cherry"]; words.sort(); // ["apple", "banana", "cherry", "kiwi"] // Custom sort by length words.sort((a, b) => a.length - b.length);
6. Slicing and Copying
# Python - Slicing numbers = [10, 20, 30, 40, 50] # Get slice first_three = numbers[0:3] # [10, 20, 30] middle = numbers[1:4] # [20, 30, 40] last_two = numbers[3:] # [40, 50] copy = numbers[:] # Full copy # Step/skip every_second = numbers[::2] # [10, 30, 50] # Reverse reversed_arr = numbers[::-1] # [50, 40, 30, 20, 10]
// JavaScript let numbers = [10, 20, 30, 40, 50]; // slice (creates new array) let firstThree = numbers.slice(0, 3); // [10, 20, 30] let lastTwo = numbers.slice(3); // [40, 50] let copy = numbers.slice(); // Full copy // Spread operator (modern way) let copy2 = [...numbers]; let firstThree2 = [...numbers.slice(0, 3)]; // Reverse (mutates original) numbers.reverse(); // [50, 40, 30, 20, 10]
Types of Arrays
1. One-Dimensional Arrays
The simplest form of arrays - a single row of elements.
# One-dimensional array scores = [85, 92, 78, 90, 88] # Visual representation: # Index: 0 1 2 3 4 # ┌────┬────┬────┬────┬────┐ # Value: │ 85 │ 92 │ 78 │ 90 │ 88 │ # └────┴────┴────┴────┴────┘
2. Multi-Dimensional Arrays
Arrays that contain other arrays.
Two-Dimensional Arrays (Matrix)
# 2D array (3 rows, 4 columns) matrix = [ [1, 2, 3, 4], # Row 0 [5, 6, 7, 8], # Row 1 [9, 10, 11, 12] # Row 2 ] # Visual representation: # col0 col1 col2 col3 # row0: [ 1, 2, 3, 4 ] # row1: [ 5, 6, 7, 8 ] # row2: [ 9, 10, 11, 12 ] # Access element at row 1, column 2 value = matrix[1][2] # 7 # Traversing 2D array for row in matrix: for element in row: print(element, end=" ") print()
// JavaScript 2D array let matrix = [ [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ]; // Access element let value = matrix[1][2]; // 7
Three-Dimensional Arrays
# 3D array (2 layers, 3 rows, 4 columns) cube = [ [ # Layer 0 [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12] ], [ # Layer 1 [13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24] ] ] # Access element at layer 1, row 2, column 3 value = cube[1][2][3] # 24
3. Jagged Arrays
Arrays where rows can have different lengths.
# Jagged array - each row has different length
jagged = [
[1, 2, 3], # Row 0: 3 elements
[4, 5], # Row 1: 2 elements
[6, 7, 8, 9] # Row 2: 4 elements
]
# Visual representation:
# Row0: [1, 2, 3]
# Row1: [4, 5]
# Row2: [6, 7, 8, 9]
# Traversing jagged array
for i, row in enumerate(jagged):
print(f"Row {i} has {len(row)} elements: {row}")
4. Dynamic Arrays (Resizable)
Most modern languages provide dynamic arrays that can grow and shrink.
# Python list (dynamic array) dynamic = [] dynamic.append(1) # [1] dynamic.append(2) # [1, 2] dynamic.append(3) # [1, 2, 3] dynamic.pop() # [1, 2]
// JavaScript array (dynamic) let dynamic = []; dynamic.push(1); // [1] dynamic.push(2); // [1, 2] dynamic.push(3); // [1, 2, 3] dynamic.pop(); // [1, 2]
// Java ArrayList (dynamic) import java.util.ArrayList; ArrayList<Integer> dynamic = new ArrayList<>(); dynamic.add(1); // [1] dynamic.add(2); // [1, 2] dynamic.add(3); // [1, 2, 3] dynamic.remove(dynamic.size() - 1); // [1, 2]
5. Character Arrays (Strings)
Strings are essentially arrays of characters.
# Python string (immutable) text = "Hello" first_char = text[0] # 'H' # Convert to list for modification chars = list(text) # ['H', 'e', 'l', 'l', 'o'] chars[0] = 'h' # ['h', 'e', 'l', 'l', 'o']
// C - strings are character arrays char text[] = "Hello"; char first_char = text[0]; // 'H' text[0] = 'h'; // "hello"
Arrays in Different Languages
Python
# Python - Lists (dynamic arrays) # Creation list1 = [1, 2, 3, 4, 5] list2 = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] list3 = [0] * 5 # [0, 0, 0, 0, 0] # List comprehension squares = [x**2 for x in range(5)] # [0, 1, 4, 9, 16] # Common operations numbers = [1, 2, 3, 4, 5] numbers.append(6) # Add to end numbers.insert(0, 0) # Insert at beginning numbers.extend([7, 8]) # Add multiple numbers.pop() # Remove last numbers.remove(3) # Remove by value numbers.clear() # Remove all # Slicing subset = numbers[1:4] # Elements 1-3 reverse = numbers[::-1] # Reverse # Methods length = len(numbers) max_val = max(numbers) min_val = min(numbers) sum_val = sum(numbers) sorted_list = sorted(numbers)
JavaScript
// JavaScript - Arrays
// Creation
let arr1 = [1, 2, 3, 4, 5];
let arr2 = new Array(5); // [empty × 5]
let arr3 = Array.from({length: 5}, (_, i) => i); // [0, 1, 2, 3, 4]
// Array methods
let numbers = [1, 2, 3, 4, 5];
// Adding/removing
numbers.push(6); // Add to end
numbers.unshift(0); // Add to beginning
numbers.pop(); // Remove from end
numbers.shift(); // Remove from beginning
numbers.splice(2, 1, 99); // Replace at index 2
// Functional methods
let doubled = numbers.map(n => n * 2);
let evens = numbers.filter(n => n % 2 === 0);
let sum = numbers.reduce((a, b) => a + b, 0);
// Searching
let index = numbers.indexOf(3);
let hasThree = numbers.includes(3);
let found = numbers.find(n => n > 3);
Java
// Java - Arrays and ArrayList
// Fixed-size array
int[] numbers = new int[5];
numbers[0] = 1;
numbers[1] = 2;
// Array initialization
int[] scores = {85, 92, 78, 90, 88};
// ArrayList (dynamic)
import java.util.ArrayList;
ArrayList<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
// Access
String first = names.get(0);
names.set(1, "Robert");
// Remove
names.remove(2);
names.remove("Alice");
// Loop
for (String name : names) {
System.out.println(name);
}
// Convert array to list
String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr);
// Convert list to array
String[] newArr = list.toArray(new String[0]);
C
// C - Static arrays
// Declaration
int numbers[5]; // Uninitialized
int scores[5] = {85, 92, 78, 90, 88}; // Initialized
int values[] = {1, 2, 3, 4, 5}; // Size inferred
// Access
numbers[0] = 10;
int x = numbers[0];
// 2D arrays
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Dynamic arrays (using malloc)
int* dynamic = (int*)malloc(5 * sizeof(int));
dynamic[0] = 10;
dynamic[1] = 20;
free(dynamic); // Don't forget to free!
// String as character array
char name[20] = "Alice";
char* greeting = "Hello";
C++
// C++ - Arrays and vectors
// Static array
int numbers[5] = {1, 2, 3, 4, 5};
// Vector (dynamic array)
#include <vector>
vector<int> vec = {1, 2, 3, 4, 5};
// Adding elements
vec.push_back(6); // Add to end
vec.insert(vec.begin(), 0); // Insert at beginning
// Removing
vec.pop_back(); // Remove last
vec.erase(vec.begin() + 2); // Remove at index 2
// Access
int first = vec[0];
int first_safe = vec.at(0); // Bounds checking
// Size
int size = vec.size();
// Iterators
for (auto it = vec.begin(); it != vec.end(); ++it) {
cout << *it << endl;
}
// Range-based for loop
for (int x : vec) {
cout << x << endl;
}
Memory Representation
How Arrays are Stored in Memory
Arrays are stored in contiguous memory locations. This is what makes array access so fast - the computer can calculate the address of any element using a simple formula.
Memory Address Calculation: Address of element i = Base Address + (i × Size of each element) Example: int numbers[5] starting at address 1000 Each int takes 4 bytes Index: 0 1 2 3 4 ┌────────┬────────┬────────┬────────┬────────┐ Address:│ 1000 │ 1004 │ 1008 │ 1012 │ 1016 │ └────────┴────────┴────────┴────────┴────────┘
Memory Layout of 2D Arrays
Two common ways to store 2D arrays in memory:
Row-Major Order (C, C++, Python, Java)
Matrix: [1, 2, 3] [4, 5, 6] [7, 8, 9] Memory layout (row by row): [1, 2, 3, 4, 5, 6, 7, 8, 9]
Column-Major Order (Fortran, MATLAB)
Memory layout (column by column): [1, 4, 7, 2, 5, 8, 3, 6, 9]
Cache Efficiency
Accessing elements sequentially (as they are stored in memory) is much faster than jumping around.
# Cache-efficient (row-major traversal) matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] for i in range(3): for j in range(3): print(matrix[i][j]) # Accesses rows sequentially # Cache-inefficient (column-major traversal) for i in range(3): for j in range(3): print(matrix[j][i]) # Jumps between rows
Common Array Algorithms
1. Linear Search
def linear_search(arr, target): """ Searches for target in array. Returns index if found, -1 otherwise. Time: O(n), Space: O(1) """ for i, value in enumerate(arr): if value == target: return i return -1 # Example numbers = [5, 2, 8, 1, 9] result = linear_search(numbers, 8) # Returns 2
2. Binary Search
def binary_search(arr, target): """ Searches for target in SORTED array. Returns index if found, -1 otherwise. Time: O(log n), Space: O(1) """ left, right = 0, len(arr) - 1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: left = mid + 1 else: right = mid - 1 return -1 # Example numbers = [1, 2, 5, 8, 9] result = binary_search(numbers, 8) # Returns 3
3. Bubble Sort
def bubble_sort(arr): """ Sorts array in ascending order. Time: O(n²), Space: O(1) """ n = len(arr) for i in range(n - 1): swapped = False for j in range(n - 1 - i): if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j] swapped = True if not swapped: break return arr # Example numbers = [5, 2, 8, 1, 9] bubble_sort(numbers) # [1, 2, 5, 8, 9]
4. Selection Sort
def selection_sort(arr): """ Sorts array in ascending order. Time: O(n²), Space: O(1) """ n = len(arr) for i in range(n - 1): min_idx = i for j in range(i + 1, n): if arr[j] < arr[min_idx]: min_idx = j arr[i], arr[min_idx] = arr[min_idx], arr[i] return arr
5. Insertion Sort
def insertion_sort(arr): """ Sorts array in ascending order. Time: O(n²), Space: O(1) """ for i in range(1, len(arr)): key = arr[i] j = i - 1 while j >= 0 and arr[j] > key: arr[j + 1] = arr[j] j -= 1 arr[j + 1] = key return arr
6. Merge Sort
def merge_sort(arr): """ Sorts array using divide and conquer. Time: O(n log n), Space: O(n) """ if len(arr) <= 1: return arr mid = len(arr) // 2 left = merge_sort(arr[:mid]) right = merge_sort(arr[mid:]) return merge(left, right) def merge(left, right): result = [] i = j = 0 while i < len(left) and j < len(right): if left[i] <= right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result.extend(left[i:]) result.extend(right[j:]) return result
7. Quick Sort
def quick_sort(arr, low=0, high=None): """ Sorts array using partition algorithm. Time: O(n log n) average, O(n²) worst Space: O(log n) """ if high is None: high = len(arr) - 1 if low < high: pi = partition(arr, low, high) quick_sort(arr, low, pi - 1) quick_sort(arr, pi + 1, high) return arr def partition(arr, low, high): pivot = arr[high] i = low - 1 for j in range(low, high): if arr[j] <= pivot: i += 1 arr[i], arr[j] = arr[j], arr[i] arr[i + 1], arr[high] = arr[high], arr[i + 1] return i + 1
8. Find Maximum and Minimum
def find_max_min(arr): """ Returns maximum and minimum in array. Time: O(n), Space: O(1) """ if not arr: return None, None max_val = min_val = arr[0] for num in arr: if num > max_val: max_val = num if num < min_val: min_val = num return max_val, min_val
9. Reverse Array
def reverse_array(arr): """ Reverses array in place. Time: O(n), Space: O(1) """ left, right = 0, len(arr) - 1 while left < right: arr[left], arr[right] = arr[right], arr[left] left += 1 right -= 1 return arr
10. Rotate Array
def rotate_array(arr, k): """ Rotates array to the right by k positions. Time: O(n), Space: O(1) """ n = len(arr) k = k % n def reverse(start, end): while start < end: arr[start], arr[end] = arr[end], arr[start] start += 1 end -= 1 reverse(0, n - 1) # Reverse entire array reverse(0, k - 1) # Reverse first k elements reverse(k, n - 1) # Reverse remaining elements return arr # Example numbers = [1, 2, 3, 4, 5] rotate_array(numbers, 2) # [4, 5, 1, 2, 3]
Advanced Array Concepts
1. Array of Objects
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# Array of objects
people = [
Person("Alice", 25),
Person("Bob", 30),
Person("Charlie", 35)
]
# Access
for person in people:
print(f"{person.name} is {person.age} years old")
# Sort by age
people.sort(key=lambda p: p.age)
2. Sparse Arrays
# Sparse array using dictionary
class SparseArray:
def __init__(self, default=0):
self.data = {}
self.default = default
def __getitem__(self, index):
return self.data.get(index, self.default)
def __setitem__(self, index, value):
if value != self.default:
self.data[index] = value
elif index in self.data:
del self.data[index]
# Usage
sparse = SparseArray()
sparse[10] = 5 # Store at index 10
sparse[1000] = 42 # Store at index 1000
print(sparse[10]) # 5
print(sparse[50]) # 0 (default)
3. Circular Array / Ring Buffer
class CircularBuffer:
def __init__(self, capacity):
self.capacity = capacity
self.buffer = [None] * capacity
self.head = 0
self.tail = 0
self.size = 0
def enqueue(self, item):
if self.size == self.capacity:
raise Exception("Buffer full")
self.buffer[self.tail] = item
self.tail = (self.tail + 1) % self.capacity
self.size += 1
def dequeue(self):
if self.size == 0:
raise Exception("Buffer empty")
item = self.buffer[self.head]
self.buffer[self.head] = None
self.head = (self.head + 1) % self.capacity
self.size -= 1
return item
4. Bit Array
class BitArray: def __init__(self, size): self.size = size self.array = [0] * ((size + 31) // 32) def set_bit(self, index): word = index // 32 bit = index % 32 self.array[word] |= (1 << bit) def clear_bit(self, index): word = index // 32 bit = index % 32 self.array[word] &= ~(1 << bit) def get_bit(self, index): word = index // 32 bit = index % 32 return (self.array[word] >> bit) & 1
Array vs Other Data Structures
Comparison Table
| Data Structure | Access | Search | Insert | Delete | Memory | Use Case |
|---|---|---|---|---|---|---|
| Array | O(1) | O(n) | O(n) | O(n) | Contiguous | Fixed-size collections |
| Dynamic Array | O(1) | O(n) | O(1)* | O(n) | Contiguous | Variable-size collections |
| Linked List | O(n) | O(n) | O(1) | O(1) | Non-contiguous | Frequent insertions/deletions |
| Hash Table | O(1) | O(1) | O(1) | O(1) | Sparse | Key-value lookups |
| Stack | O(1) | O(n) | O(1) | O(1) | Contiguous | LIFO operations |
| Queue | O(1) | O(n) | O(1) | O(1) | Contiguous | FIFO operations |
*Amortized O(1) for append operations
When to Use Arrays
Use arrays when:
- You need fast indexed access
- The size is known in advance or doesn't change much
- Memory efficiency is important
- You need to store homogeneous data
- Cache locality matters (access patterns are sequential)
Use other data structures when:
- Frequent insertions/deletions (Linked List)
- Need key-value pairs (Hash Table)
- Unknown size and dynamic growth (Dynamic Array)
- LIFO operations (Stack)
- FIFO operations (Queue)
Practical Examples
Example 1: Student Grade Management
class GradeManager:
def __init__(self):
self.students = []
self.grades = []
def add_student(self, name):
self.students.append(name)
self.grades.append([])
print(f"Added student: {name}")
def add_grade(self, student_name, grade):
try:
index = self.students.index(student_name)
self.grades[index].append(grade)
print(f"Added grade {grade} for {student_name}")
except ValueError:
print(f"Student {student_name} not found")
def get_average(self, student_name):
try:
index = self.students.index(student_name)
grades = self.grades[index]
if not grades:
return 0
return sum(grades) / len(grades)
except ValueError:
return None
def get_class_average(self):
total_sum = 0
total_count = 0
for grades in self.grades:
total_sum += sum(grades)
total_count += len(grades)
return total_sum / total_count if total_count > 0 else 0
def get_top_student(self):
averages = [(self.students[i], self.get_average(self.students[i]))
for i in range(len(self.students))]
return max(averages, key=lambda x: x[1]) if averages else None
# Usage
manager = GradeManager()
manager.add_student("Alice")
manager.add_student("Bob")
manager.add_grade("Alice", 85)
manager.add_grade("Alice", 90)
manager.add_grade("Bob", 78)
print(f"Alice's average: {manager.get_average('Alice')}")
print(f"Class average: {manager.get_class_average()}")
print(f"Top student: {manager.get_top_student()}")
Example 2: Matrix Operations
class Matrix:
def __init__(self, rows, cols, data=None):
self.rows = rows
self.cols = cols
if data:
self.data = data
else:
self.data = [[0] * cols for _ in range(rows)]
def __add__(self, other):
if self.rows != other.rows or self.cols != other.cols:
raise ValueError("Matrices must have same dimensions")
result = Matrix(self.rows, self.cols)
for i in range(self.rows):
for j in range(self.cols):
result.data[i][j] = self.data[i][j] + other.data[i][j]
return result
def __mul__(self, other):
if self.cols != other.rows:
raise ValueError("Invalid dimensions for multiplication")
result = Matrix(self.rows, other.cols)
for i in range(self.rows):
for j in range(other.cols):
total = 0
for k in range(self.cols):
total += self.data[i][k] * other.data[k][j]
result.data[i][j] = total
return result
def transpose(self):
result = Matrix(self.cols, self.rows)
for i in range(self.rows):
for j in range(self.cols):
result.data[j][i] = self.data[i][j]
return result
def display(self):
for row in self.data:
print(" ".join(f"{val:4}" for val in row))
# Usage
A = Matrix(2, 3, [[1, 2, 3], [4, 5, 6]])
B = Matrix(3, 2, [[7, 8], [9, 10], [11, 12]])
print("Matrix A:")
A.display()
print("\nMatrix B:")
B.display()
print("\nA × B:")
(A * B).display()
Example 3: Image Processing (Grayscale)
class ImageProcessor:
def __init__(self, width, height):
self.width = width
self.height = height
self.pixels = [[0] * width for _ in range(height)]
def set_pixel(self, x, y, value):
if 0 <= x < self.width and 0 <= y < self.height:
self.pixels[y][x] = max(0, min(255, value))
def get_pixel(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
return self.pixels[y][x]
return 0
def apply_filter(self, kernel):
k_size = len(kernel)
offset = k_size // 2
result = ImageProcessor(self.width, self.height)
for y in range(self.height):
for x in range(self.width):
total = 0
for ky in range(k_size):
for kx in range(k_size):
px = x + kx - offset
py = y + ky - offset
if 0 <= px < self.width and 0 <= py < self.height:
total += self.pixels[py][px] * kernel[ky][kx]
result.set_pixel(x, y, int(total))
return result
def blur(self):
kernel = [
[1, 1, 1],
[1, 1, 1],
[1, 1, 1]
]
factor = 9
kernel = [[k / factor for k in row] for row in kernel]
return self.apply_filter(kernel)
def edge_detect(self):
kernel = [
[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]
]
return self.apply_filter(kernel)
def display_ascii(self):
chars = " .:-=+*#%@"
for row in self.pixels:
line = "".join(chars[val * len(chars) // 256] for val in row)
print(line)
# Usage
img = ImageProcessor(40, 20)
# Draw a simple circle
for y in range(20):
for x in range(40):
if (x - 20) ** 2 + (y - 10) ** 2 < 64:
img.set_pixel(x, y, 255)
print("Original:")
img.display_ascii()
print("\nBlurred:")
img.blur().display_ascii()
Common Mistakes and Pitfalls
1. Off-by-One Errors
# ❌ Wrong - accessing index out of bounds arr = [1, 2, 3] for i in range(len(arr) + 1): # Will try to access arr[3] print(arr[i]) # ✅ Correct for i in range(len(arr)): print(arr[i])
2. Modifying Array While Iterating
# ❌ Wrong - skipping elements arr = [1, 2, 3, 4, 5] for i in range(len(arr)): if arr[i] % 2 == 0: arr.pop(i) # This will skip next element # ✅ Correct - iterate backwards for i in range(len(arr) - 1, -1, -1): if arr[i] % 2 == 0: arr.pop(i) # ✅ Or use list comprehension arr = [x for x in arr if x % 2 != 0]
3. Shallow Copy vs Deep Copy
# ❌ Wrong - creates shallow copy original = [[1, 2], [3, 4]] copy = original[:] # Shallow copy copy[0][0] = 99 print(original[0][0]) # 99 - original changed! # ✅ Correct - creates deep copy import copy original = [[1, 2], [3, 4]] copy = copy.deepcopy(original) copy[0][0] = 99 print(original[0][0]) # 1 - original unchanged
4. Array Size Issues
# ❌ Wrong - trying to add beyond capacity in fixed-size array arr = [1, 2, 3] arr[3] = 4 # IndexError # ✅ Correct - use append or create new array arr.append(4) # If using list
5. Using Arrays for Key-Value Lookups
# ❌ Wrong - inefficient
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
def get_age(name):
for i, n in enumerate(names):
if n == name:
return ages[i]
return None
# ✅ Correct - use dictionary
person = {"Alice": 25, "Bob": 30, "Charlie": 35}
age = person.get("Alice")
Interview Questions
Q1: Two Sum
Find two numbers in an array that add up to a target.
def two_sum(nums, target):
"""
Time: O(n), Space: O(n)
"""
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []
# Example
nums = [2, 7, 11, 15]
target = 9
print(two_sum(nums, target)) # [0, 1]
Q2: Maximum Subarray (Kadane's Algorithm)
def max_subarray(nums): """ Find contiguous subarray with largest sum. Time: O(n), Space: O(1) """ max_current = max_global = nums[0] for i in range(1, len(nums)): max_current = max(nums[i], max_current + nums[i]) max_global = max(max_global, max_current) return max_global # Example nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] print(max_subarray(nums)) # 6 (subarray [4, -1, 2, 1])
Q3: Rotate Image (90 degrees)
def rotate_matrix(matrix): """ Rotate N x N matrix 90 degrees clockwise. Time: O(n²), Space: O(1) """ n = len(matrix) # Transpose for i in range(n): for j in range(i + 1, n): matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] # Reverse each row for i in range(n): matrix[i].reverse() return matrix # Example matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ] print(rotate_matrix(matrix)) # [[7, 4, 1], # [8, 5, 2], # [9, 6, 3]]
Q4: Find Duplicates
def find_duplicates(nums): """ Find all duplicates in array where values are in range 1 to n. Time: O(n), Space: O(1) """ duplicates = [] for num in nums: index = abs(num) - 1 if nums[index] < 0: duplicates.append(abs(num)) else: nums[index] = -nums[index] return duplicates # Example nums = [4, 3, 2, 7, 8, 2, 3, 1] print(find_duplicates(nums)) # [2, 3]
Q5: Merge Sorted Arrays
def merge_sorted(arr1, arr2): """ Merge two sorted arrays. Time: O(m + n), Space: O(m + n) """ result = [] i = j = 0 while i < len(arr1) and j < len(arr2): if arr1[i] < arr2[j]: result.append(arr1[i]) i += 1 else: result.append(arr2[j]) j += 1 result.extend(arr1[i:]) result.extend(arr2[j:]) return result # Example arr1 = [1, 3, 5, 7] arr2 = [2, 4, 6, 8] print(merge_sorted(arr1, arr2)) # [1, 2, 3, 4, 5, 6, 7, 8]
Q6: Product of Array Except Self
def product_except_self(nums): """ Return array where each element is product of all other elements. Time: O(n), Space: O(1) excluding output """ n = len(nums) result = [1] * n # Left products left = 1 for i in range(n): result[i] = left left *= nums[i] # Right products right = 1 for i in range(n - 1, -1, -1): result[i] *= right right *= nums[i] return result # Example nums = [1, 2, 3, 4] print(product_except_self(nums)) # [24, 12, 8, 6]
Q7: First Missing Positive
def first_missing_positive(nums): """ Find smallest missing positive integer. Time: O(n), Space: O(1) """ n = len(nums) # Place numbers in correct positions for i in range(n): while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]: nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1] # Find first missing for i in range(n): if nums[i] != i + 1: return i + 1 return n + 1 # Example nums = [3, 4, -1, 1] print(first_missing_positive(nums)) # 2
Conclusion
Arrays are fundamental to programming and serve as building blocks for more complex data structures. Understanding arrays thoroughly is essential for any programmer.
Key Takeaways
- Arrays store elements in contiguous memory for efficient access
- Index-based access is O(1) - constant time
- Searching is O(n) for unsorted arrays, O(log n) for sorted
- Insertion and deletion are O(n) for arrays
- Memory efficiency is high due to no overhead
- Cache locality makes sequential access very fast
Best Practices
- ✅ Use appropriate array size to avoid waste
- ✅ Consider dynamic arrays for unknown sizes
- ✅ Use list comprehensions in Python for cleaner code
- ✅ Be mindful of off-by-one errors
- ✅ Understand time complexity of operations
- ✅ Use appropriate data structures for your needs
Quick Reference Card
| Operation | Syntax | Complexity |
|---|---|---|
| Access | arr[i] | O(1) |
| Update | arr[i] = x | O(1) |
| Append | arr.append(x) | O(1)* |
| Insert | arr.insert(i, x) | O(n) |
| Delete | arr.pop(i) | O(n) |
| Search | x in arr | O(n) |
| Sort | arr.sort() | O(n log n) |
| Length | len(arr) | O(1) |
*Amortized for dynamic arrays
Arrays are everywhere in programming. Master them, and you'll have a solid foundation for tackling more advanced programming concepts!