two-dimensional array
adjacent elements
array manipulation
programming
data structures

Get adjacent elements in a two-dimensional array?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

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.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.