two-dimensional array
adjacent elements
array manipulation
programming
data structures

Get adjacent elements in a two-dimensional array?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Neighbor lookup in a two-dimensional array is a basic building block for grid algorithms. You need it for maze search, flood fill, game logic, image processing, and shortest-path problems. Most bugs come from boundary errors, inconsistent coordinate conventions, or using the wrong adjacency model.

Define Grid Coordinates and Bounds

Use one coordinate convention and keep it everywhere. In Python, the common model is (row, col).

A neighbor coordinate is valid only when both indices stay inside bounds.

python
1def in_bounds(rows: int, cols: int, r: int, c: int) -> bool:
2    return 0 <= r < rows and 0 <= c < cols
3
4print(in_bounds(3, 4, 2, 1))
5print(in_bounds(3, 4, -1, 0))

Having one reusable bounds function reduces repeated mistakes.

Four-Direction Adjacency

Four-direction movement includes up, down, left, and right only.

python
1def neighbors4(grid, r, c):
2    rows, cols = len(grid), len(grid[0])
3    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
4
5    result = []
6    for dr, dc in directions:
7        nr, nc = r + dr, c + dc
8        if 0 <= nr < rows and 0 <= nc < cols:
9            result.append((nr, nc, grid[nr][nc]))
10    return result
11
12matrix = [
13    [1, 2, 3],
14    [4, 5, 6],
15    [7, 8, 9],
16]
17
18print(neighbors4(matrix, 1, 1))
19print(neighbors4(matrix, 0, 0))

Use this model for problems where diagonal motion is disallowed.

Eight-Direction Adjacency

Eight-direction movement adds diagonals.

python
1def neighbors8(grid, r, c):
2    rows, cols = len(grid), len(grid[0])
3    result = []
4
5    for dr in (-1, 0, 1):
6        for dc in (-1, 0, 1):
7            if dr == 0 and dc == 0:
8                continue
9            nr, nc = r + dr, c + dc
10            if 0 <= nr < rows and 0 <= nc < cols:
11                result.append((nr, nc, grid[nr][nc]))
12
13    return result
14
15print(neighbors8(matrix, 1, 1))
16print(neighbors8(matrix, 2, 0))

Use this model for cellular automata or image kernels where diagonal influence is required.

Build a Reusable Neighbor Generator

Most grid algorithms need coordinates only. A generator avoids unnecessary list allocation in tight loops.

python
1def iter_neighbors(rows, cols, r, c, diagonals=False):
2    base = [(-1, 0), (1, 0), (0, -1), (0, 1)]
3    diag = [(-1, -1), (-1, 1), (1, -1), (1, 1)]
4    moves = base + diag if diagonals else base
5
6    for dr, dc in moves:
7        nr, nc = r + dr, c + dc
8        if 0 <= nr < rows and 0 <= nc < cols:
9            yield nr, nc
10
11for nr, nc in iter_neighbors(3, 3, 1, 1, diagonals=True):
12    print(nr, nc)

This helper can be reused in BFS, DFS, Dijkstra, and A-star implementations.

Example: BFS With Neighbor Helper

Here is a runnable shortest-path example on an unweighted grid with blocked cells.

python
1from collections import deque
2
3
4def shortest_path_steps(grid, start, goal):
5    rows, cols = len(grid), len(grid[0])
6    q = deque([(start[0], start[1], 0)])
7    seen = {start}
8
9    while q:
10        r, c, dist = q.popleft()
11        if (r, c) == goal:
12            return dist
13
14        for nr, nc in iter_neighbors(rows, cols, r, c, diagonals=False):
15            if grid[nr][nc] == 1:  # blocked
16                continue
17            if (nr, nc) in seen:
18                continue
19            seen.add((nr, nc))
20            q.append((nr, nc, dist + 1))
21
22    return -1
23
24board = [
25    [0, 0, 1, 0],
26    [1, 0, 1, 0],
27    [0, 0, 0, 0],
28]
29
30print(shortest_path_steps(board, (0, 0), (2, 3)))

A single tested neighbor helper keeps traversal logic clean.

Performance Notes

For large grids, neighbor lookup runs many times. Practical optimizations:

  • Define direction arrays once, not inside inner loops.
  • Yield coordinates instead of full tuples with copied values.
  • Avoid converting between (x, y) and (row, col) repeatedly.

These improvements matter in simulation and pathfinding workloads.

Common Pitfalls

  • Accessing neighbors without bounds checks. Fix: centralize bounds logic and reuse it.
  • Mixing coordinate order across functions. Fix: standardize on (row, col) and document it.
  • Including the current cell as its own neighbor in 8-direction loops. Fix: skip the dr == 0 and dc == 0 case.
  • Using diagonal movement when problem rules allow only 4-direction movement. Fix: select adjacency model from problem statement first.
  • Reallocating neighbor lists in hot loops. Fix: use generators or fixed direction arrays for better performance.

Summary

  • Neighbor lookup is fundamental for most grid algorithms.
  • Choose 4-direction or 8-direction adjacency based on domain rules.
  • Use shared bounds checks and neighbor helpers to reduce bugs.
  • Generators improve performance and keep traversal code cleaner.
  • Consistent coordinate conventions are critical for correctness.

Course illustration
Course illustration

All Rights Reserved.