array operations
element comparison
algorithm efficiency
minimal difference
array processing

Given two arrays A and Q, foreach element of of Q, find the element in A with smallest difference

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

If you need, for every query value in Q, the element in A with the smallest absolute difference, the naive approach is easy but slow. The standard optimization is to sort A once and use binary search for each query, which turns repeated nearest-value lookups into an efficient and predictable routine.

The Naive Approach

The direct method checks every element of A for every value in Q.

python
1def nearest_naive(a, q):
2    result = []
3    for x in q:
4        best = min(a, key=lambda value: abs(value - x))
5        result.append(best)
6    return result
7
8print(nearest_naive([10, 3, 20, 8], [2, 9, 17, 30]))

This is fine for tiny inputs, but it costs O(len(A) * len(Q)), which becomes expensive when both arrays are large.

A faster method is:

  1. sort A
  2. for each query, find where it would be inserted
  3. compare the closest left and right neighbors

That reduces each query to O(log n) after the initial sort.

python
1from bisect import bisect_left
2
3
4def nearest_values(a, q):
5    if not a:
6        raise ValueError("A must not be empty")
7
8    sorted_a = sorted(a)
9    result = []
10
11    for x in q:
12        i = bisect_left(sorted_a, x)
13
14        if i == 0:
15            result.append(sorted_a[0])
16            continue
17
18        if i == len(sorted_a):
19            result.append(sorted_a[-1])
20            continue
21
22        left = sorted_a[i - 1]
23        right = sorted_a[i]
24
25        if abs(x - left) <= abs(right - x):
26            result.append(left)
27        else:
28            result.append(right)
29
30    return result
31
32
33print(nearest_values([10, 3, 20, 8], [2, 9, 17, 30]))

This is the standard efficient answer for many nearest-element lookup problems.

Why Binary Search Works Here

Once A is sorted, the closest value to x must be near the insertion point where x would fit in order. That means you do not need to scan the whole array. You only need to check at most two candidates:

  • the neighbor just to the left
  • the neighbor just to the right

That is the key observation that makes the algorithm fast.

Tie Handling Is a Design Choice

What if the query is equally close to two values?

For example, with A = [4, 8] and query 6, both choices are distance 2 away.

The code above uses this rule:

python
if abs(x - left) <= abs(right - x):
    result.append(left)

That means ties prefer the left neighbor. You can change the rule if your application prefers the larger value, the first-seen value, or even both candidates.

A Reusable Lookup Helper

If A stays fixed and you have many query batches, wrap the sorted array in a small helper object.

python
1from bisect import bisect_left
2
3
4class NearestLookup:
5    def __init__(self, values):
6        if not values:
7            raise ValueError("values must not be empty")
8        self.values = sorted(values)
9
10    def nearest(self, x):
11        i = bisect_left(self.values, x)
12        if i == 0:
13            return self.values[0]
14        if i == len(self.values):
15            return self.values[-1]
16
17        left = self.values[i - 1]
18        right = self.values[i]
19        return left if abs(x - left) <= abs(right - x) else right
20
21
22lookup = NearestLookup([5, 14, 21, 33])
23print(lookup.nearest(18))

This avoids resorting A for every query set.

What About NumPy?

For large numeric workloads, NumPy can make the code more compact and often faster by vectorizing parts of the logic. The same idea still applies: sort the base array and use a search operation around the insertion points.

The algorithmic insight does not change. Vectorization just changes the implementation style.

Common Pitfalls

The biggest mistake is forgetting to sort A before using binary search. Without sorting, the insertion-point logic is meaningless.

Another issue is failing to handle boundary cases where the query falls before the first element or after the last element.

Developers also sometimes sort A repeatedly for every query batch, which throws away the performance benefit.

Finally, tie behavior should be explicit. If two values are equally close, choose a rule and document it so the output is predictable.

Summary

  • The naive solution checks every value in A for every query in Q.
  • A faster solution sorts A once and uses binary search for each query.
  • After sorting, the nearest value must be around the insertion point.
  • Boundary handling and tie handling should be defined explicitly.
  • If A is reused often, keep a sorted copy and query it repeatedly.

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