Find All Symmetric Pairs in an Array
Two approaches to find all symmetric pairs in an array: O(n^2) brute force and O(n) HashMap, with working C++, Python, and Java code.
Two pairs (a, b) and (c, d) are symmetric when a == d and b == c. The problem is to find all such pairs in a given array. It shows up in hash-map sections of coding screens because it tests one specific skill: recognising when a look-up table eliminates the need for a nested loop.
What Makes Two Pairs Symmetric?
The definition is compact. Given a 2D array where each element is a pair of integers, two pairs at different positions are symmetric if swapping the values of one produces the other.
Consider this canonical input:
arr = { {1, 2}, {3, 4}, {5, 6}, {2, 1}, {4, 3}, {10, 11} }
Checking each pair manually:
(1, 2)and(2, 1): swap gives(2, 1), which is in the array. Symmetric.(3, 4)and(4, 3): swap gives(4, 3), which is in the array. Symmetric.(5, 6): the pair(6, 5)is absent. Not symmetric.(10, 11): the pair(11, 10)is absent. Not symmetric.
Expected output:
(1, 2) and (2, 1) are symmetric
(3, 4) and (4, 3) are symmetric
Verify every implementation against this example before moving to edge cases.
Method 1: Brute Force with Nested Loops
The direct reading of the problem leads naturally to a nested-loop solution. For each pair at index i, scan every subsequent pair at index j to check whether the two are symmetric.
Algorithm steps
- Start the outer loop at index
i = 0. - Start the inner loop at index
j = i + 1(avoids double-counting and self-comparison). - For each combination, check two conditions:
arr[i][0] == arr[j][1]ANDarr[i][1] == arr[j][0]. - If both hold, the pair at
iand the pair atjare symmetric.
C++ implementation
#include <iostream>
#include <vector>
using namespace std;
void findSymmetricPairsBrute(vector<pair<int,int>>& arr) {
int n = arr.size();
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (arr[i].first == arr[j].second &&
arr[i].second == arr[j].first) {
cout << "(" << arr[i].first << "," << arr[i].second
<< ") and ("
<< arr[j].first << "," << arr[j].second
<< ") are symmetric\n";
}
}
}
}
int main() {
vector<pair<int,int>> arr = {
{1,2},{3,4},{5,6},{2,1},{4,3},{10,11}
};
findSymmetricPairsBrute(arr);
return 0;
}
Output:
(1,2) and (2,1) are symmetric
(3,4) and (4,3) are symmetric
Python implementation
def find_symmetric_pairs_brute(pairs):
n = len(pairs)
for i in range(n):
for j in range(i + 1, n):
if pairs[i][0] == pairs[j][1] and pairs[i][1] == pairs[j][0]:
print(f"({pairs[i][0]},{pairs[i][1]}) and "
f"({pairs[j][0]},{pairs[j][1]}) are symmetric")
pairs = [(1,2),(3,4),(5,6),(2,1),(4,3),(10,11)]
find_symmetric_pairs_brute(pairs)
Complexity
- Time: O(n^2) — for n = 6 pairs, the loop runs 15 comparisons (n*(n-1)/2 = 6*5/2). For n = 100, that is 4,950. For n = 10,000, it is 49,995,000.
- Space: O(1) — no auxiliary data structure beyond loop variables.
The O(1) space is attractive, but the quadratic growth in comparisons makes this approach slow for any interview input above a few hundred pairs.
Method 2: HashMap in O(n) Time
The key observation: instead of searching for the symmetric partner on each iteration, store previously seen pairs in a hash map. When a new pair (a, b) arrives, check whether the map already holds an entry where key b maps to value a. If yes, (b, a) was seen earlier and the two are symmetric. If no, store a to b in the map and move on.
This reduces the problem to one left-to-right scan.
Algorithm steps
- Create an empty hash map.
- For each pair
(a, b)in the array:- Look up key
bin the map. - If the map contains key
band its value equalsa: found a symmetric pair. Print it. - Otherwise: store
aas key withbas value. Continue.
- Look up key
Step-by-step trace
| Step | Pair | Map lookup | Outcome |
|---|---|---|---|
| 1 | (1, 2) | key 2 not in map | store 1 to 2 |
| 2 | (3, 4) | key 4 not in map | store 3 to 4 |
| 3 | (5, 6) | key 6 not in map | store 5 to 6 |
| 4 | (2, 1) | key 1 in map, value = 2 | print (1,2) and (2,1) |
| 5 | (4, 3) | key 3 in map, value = 4 | print (3,4) and (4,3) |
| 6 | (10, 11) | key 11 not in map | store 10 to 11 |
Each pair is touched exactly once. The output matches the expected result.
C++ implementation
C++ provides unordered_map for O(1) average-case lookup and insertion:
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
void findSymmetricPairsHash(vector<pair<int,int>>& arr) {
unordered_map<int, int> seen;
for (auto& p : arr) {
int a = p.first, b = p.second;
auto it = seen.find(b);
if (it != seen.end() && it->second == a) {
cout << "(" << b << "," << a << ") and ("
<< a << "," << b << ") are symmetric\n";
} else {
seen[a] = b;
}
}
}
int main() {
vector<pair<int,int>> arr = {
{1,2},{3,4},{5,6},{2,1},{4,3},{10,11}
};
findSymmetricPairsHash(arr);
return 0;
}
Python implementation
Python’s built-in dict is an unordered hash map. The logic is identical:
def find_symmetric_pairs(pairs):
seen = {}
for a, b in pairs:
if b in seen and seen[b] == a:
print(f"({b},{a}) and ({a},{b}) are symmetric")
else:
seen[a] = b
pairs = [(1,2),(3,4),(5,6),(2,1),(4,3),(10,11)]
find_symmetric_pairs(pairs)
Java implementation
import java.util.HashMap;
import java.util.Map;
public class SymmetricPairs {
static void findSymmetricPairs(int[][] arr) {
Map<Integer, Integer> seen = new HashMap<>();
for (int[] pair : arr) {
int a = pair[0], b = pair[1];
if (seen.containsKey(b) && seen.get(b).equals(a)) {
System.out.println("(" + b + "," + a + ") and ("
+ a + "," + b + ") are symmetric");
} else {
seen.put(a, b);
}
}
}
public static void main(String[] args) {
int[][] arr = { {1,2},{3,4},{5,6},{2,1},{4,3},{10,11} };
findSymmetricPairs(arr);
}
}
One Java-specific detail: seen.get(b) returns a boxed Integer, not a primitive int. The .equals(a) call handles the comparison correctly. Relying on == with boxed integers works only for values in the cached range of -128 to 127; outside that range it compares object references, not values.
Complexity
- Time: O(n) — one pass; each hash map operation is O(1) average.
- Space: O(n) — the map can hold at most n entries in the worst case (no symmetric pairs at all).
Complexity Comparison
| Approach | Time | Space | Suitable for |
|---|---|---|---|
| Nested loops | O(n^2) | O(1) | n below 1,000 |
| HashMap single pass | O(n) | O(n) | n into the millions |
For any placement coding screen, the HashMap approach is the expected answer. Starting with brute force and explaining why it fails at scale is a good technique: it shows the interviewer that you understand the cost before you fix it.
Edge Cases
Three situations are worth testing before a submission:
Self-symmetric pairs
The pair (a, a) is symmetric with itself. A single occurrence in the array is stored in the map but not matched (no prior entry exists). A second occurrence of (a, a) triggers a match. Both methods handle this correctly by design.
No symmetric pairs
Input: {(1, 2), (3, 4), (5, 6)}. No pair has a symmetric partner. Both algorithms produce no output. This is valid and expected.
Duplicate non-symmetric pairs
If (a, b) appears twice and (b, a) also appears, the HashMap stores the first (a, b), then overwrites it if (b, a) has not appeared yet. Standard implementations assume distinct pairs. If the input can contain duplicates, extend the map to hold a list of values per key rather than a single value.
These cases connect directly to insert, delete, and search in an array and to finding the smallest and largest element. Both cover the array-operations groundwork that symmetric-pair detection builds on. The same two-index traversal also appears in matrix addition, subtraction, and multiplication, the cluster pillar for 2D array problems. For a wider set of hash-map and data-structure programs across C++, Java, and Python, the FACE Prep data structures programs collection covers the full scope.
The dict-based look-up behind the O(n) solution is the same pattern that powers LLM token caches and API response indexes. TinkerLLM is where you apply that instinct to real model interactions, starting at ₹299 with no subscription required.
Primary sources
Frequently asked questions
What are symmetric pairs in an array?
Two pairs (a, b) and (c, d) are symmetric when a equals d and b equals c. For example, (1, 2) and (2, 1) are symmetric because swapping the elements of either one produces the other.
What is the time complexity of the HashMap approach?
O(n) time and O(n) space, where n is the number of pairs. Each pair is processed in one left-to-right pass through the array; hash map lookups and inserts are O(1) on average.
Does the brute force approach work for large inputs?
It works but runs in O(n^2) time. For n = 10,000 pairs, that means up to 49,995,000 comparisons. The HashMap approach handles the same input in a single pass of 10,000 operations.
What happens when a pair is self-symmetric like (a, a)?
A single occurrence of (a, a) is not matched in the HashMap approach — the first time it appears, it is stored; no prior entry matches. Two occurrences of (a, a) will match on the second occurrence. Both approaches handle this correctly.
Is this problem asked in placement interviews?
Yes. It appears in the hash-map section of coding screens at service-tier companies. Interviewers typically expect you to start with the brute-force O(n^2) solution and then optimise to O(n) using a hash map within the same session.
A self-paced playground for building with LLMs.
TinkerLLM is FACE Prep's sister property. A guided environment for shipping real LLM applications, the kind of project that earns a paragraph on your resume, not a line.
Try TinkerLLM (₹299 launch)