Hello again. Last lesson established how to derive time and auxiliary-space complexity from Java code: sequential work adds, nested work may multiply, dependent bounds require counting, and growing allocations determine space cost.
Now we use that analysis for the decision that matters in an interview: which correct solution should you choose, and why? You will compare solutions not only by asymptotic time and space, but also by input-mutation rules, output requirements, implementation risk, and the real costs of Java data structures. This is the bridge between recognizing code and proposing a credible alternative.
Complexity is evidence, not the entire decision
Suppose an interviewer asks:
Given an integer array
numsand a target, return the indices of two distinct elements whose values add to the target.
Before comparing algorithms, pin down the contract. A small wording change can make a different solution preferable:
- Must you return original indices, or only determine whether a pair exists?
- May you modify the input array by sorting it?
- Is extra memory allowed?
- Is there guaranteed to be one valid pair?
- Are duplicate values allowed, such as
[3, 3]with target6?
A strong interview process is:
- State a simple correct baseline.
- Analyze its time and space.
- Identify repeated work or missing information.
- Offer an improved approach.
- Compare the approaches against the actual contract.
The important principle is not “always choose the smallest Big O.” It is:
Choose the solution that satisfies the contract with the best justified trade-off.
For most large, arbitrary inputs, a lower growth rate matters enormously. If input size doubles:
| Growth rate | Approximate change in work when doubles |
|---|---|
| times | |
| Slightly more than times | |
| times | |
| times |

Asymptotic notation deliberately ignores constant factors, but constants are not imaginary. They become relevant when input sizes are modest, memory is tight, or one implementation has substantially more allocation and bookkeeping than another.
One problem, three legitimate approaches
1. Brute force: simple, correct, and quadratic
The direct approach checks each distinct pair.
public static int[] twoSumBruteForce(int[] nums, int target) {
for (int i = 0; i < nums.length - 1; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
return new int[] {i, j};
}
}
}
throw new IllegalArgumentException("No valid pair");
}
The inner loop considers later positions only, so it avoids checking the same pair twice. As you saw last lesson, it performs roughly
pair comparisons in the no-match case. Its complexity is therefore:
The early return may make some individual inputs fast, but the worst case is still quadratic: the pair could be near the end, or no pair may exist.
This baseline is not “wrong.” It has real strengths:
- It is easy to explain and implement correctly.
- It preserves the original array.
- It naturally returns original indices.
- It uses almost no extra memory.
- It may be entirely acceptable for a known small input bound.
Its weakness is scale. Every new value is repeatedly compared with many other values. The algorithm has no memory of work already done.
2. Hash map: trade memory for expected linear time
The key observation is algebraic. When you inspect a value , the only value that can complete the pair is:
Instead of scanning the array again to find needed, store each previously seen value and its index in a HashMap.
public static int[] twoSumHashMap(int[] nums, int target) {
Map<Integer, Integer> indexByValue = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int needed = target - nums[i];
Integer partnerIndex = indexByValue.get(needed);
if (partnerIndex != null) {
return new int[] {partnerIndex, i};
}
indexByValue.put(nums[i], i);
}
throw new IllegalArgumentException("No valid pair");
}
Notice the order: look up first, then store the current value. This prevents one array element from being matched with itself. It also handles [3, 3] correctly: the first 3 is stored, and the second finds it.
Under the usual assumption that hash-map lookups and insertions take expected constant time:
This is often the preferred interview answer for the “return indices from an unsorted array” version of Two Sum. It makes one pass, preserves the input, and keeps the original index alongside each value.
However, explain the qualification expected. Hashing depends on reasonable hash distribution and implementation behavior; it is not the same kind of unconditional guarantee as directly indexing an array. In ordinary coding-interview discussion, “expected time and space” is the appropriate statement.
3. Sort and use two pointers: less extra storage, different contract
If the task asks only whether a pair exists, sorting creates useful order. Put one pointer at each end:
- If the sum is too small, move the left pointer right to increase the sum.
- If the sum is too large, move the right pointer left to decrease the sum.
- If the sum equals the target, a pair exists.
Once sorted, each pointer only moves inward, so the two-pointer scan is . But sorting dominates:
If sorting the provided int[] is permitted, the algorithm can use little additional algorithmic storage beyond the sort’s own implementation needs. If mutation is forbidden, you must copy the array before sorting:
There is a more important practical limitation: after sorting raw values, the original indices are lost. You can preserve them by sorting value-index pairs, but that introduces extra storage and more code. For the index-returning form of Two Sum, the hash-map solution is usually clearer and asymptotically faster.
The following comparison is worth being able to say aloud:
| Approach | Time | Auxiliary space | Modifies input? | Original indices easy? |
|---|---|---|---|---|
| Nested-loop baseline | No | Yes | ||
| Hash map | Expected | No | Yes | |
| Sort plus two pointers | Often low if mutation allowed; if copied | Usually yes | No, unless extra bookkeeping is added |
The Two Sum walkthrough from take U forward presents these same alternatives and their intended use cases.
2 Sum Problem | 2 types of the same problem for Interviews | Brute-Better-Optimal
Watch “2 Sum Problem | 2 types of the same problem for Interviews | Brute-Better-Optimal” from take U forward to see how the brute-force, hash-map, and sorted two-pointer solutions arise from the same problem.
First watch the hash approach. Focus on the complement calculation, why previously seen values are stored with their indices, and why the map changes repeated searching into one pass. Then watch the two pointers. Focus on how sorting makes pointer movements logically safe, and on the distinction between a yes-or-no result and returning original indices.
Practical trade-offs that Big O hides
A statement such as “ beats ” is generally right at scale, but incomplete. In a real Java program, compare these dimensions explicitly.
Memory and allocation
A HashMap<Integer, Integer> stores data for up to values. Beyond the logical space classification, Java also has practical overhead from map entries, buckets, resizing, and boxed Integer values. This is usually a worthwhile trade for a large unsorted search, but it can be unacceptable in a memory-constrained service or when the input is already enormous.
Sorting a primitive int[], by contrast, tends to work with compact contiguous data. Its asymptotic time is worse than hashing, but it may have favorable cache behavior and lower memory pressure. That does not reverse the asymptotic conclusion for large general inputs; it explains why measured performance and constraints still matter.
Mutation and ownership
Sorting is not just a technical operation. It changes data that the caller may still need in its original order.
In an interview, do not quietly sort an input array. State the decision:
“Sorting gives me time and a linear two-pointer scan afterward, but it mutates the input. If the input must remain unchanged, I would sort a copy, which adds space.”
This demonstrates awareness of API behavior, not merely algorithm patterns.
Output requirements
An algorithm can solve a related problem but fail the requested one.
- If the output is a boolean, sorting plus two pointers may be attractive when extra memory is restricted.
- If the output is original indices, a hash map naturally retains them.
- If all matching pairs are required rather than one pair, duplicate handling and output size become central to the design.
Always compare alternatives against the exact required output.
Implementation risk
The hash-map approach is short, but has details worth checking:
- Look up before inserting to avoid using one element twice.
- Decide what to do with duplicate values.
- State what happens if no solution exists.
- Confirm whether integer-value constraints make overflow relevant.
The sorted two-pointer approach requires a different set of checks:
- Is input mutation permitted?
- Does sorting destroy required index information?
- Is the loop condition
left < rightso the same element cannot be used twice? - Does each pointer movement preserve the reasoning established by sorted order?
In an interview, a slightly slower solution that you can implement and test correctly is better than a nominally optimal solution with unexamined edge cases. Still, once a clearly scalable solution exists and the constraints allow it, you should propose it.
Data-structure choice is part of the algorithm
Two solutions can have the same high-level idea but radically different operational costs because they use different collections.
For example, imagine that an algorithm repeatedly accesses elements by numeric position and sometimes inserts or removes elements. ArrayList and LinkedList both implement List, but they favor different operations. Oracle’s Java Tutorials emphasize that ArrayList provides constant-time positional access and is generally fast, while LinkedList has linear-time positional access and can carry a large practical overhead despite constant-time changes at its ends.
List Implementations - Java™ Tutorials
Read Oracle’s “List Implementations” page to connect asymptotic operation costs with a practical Java-library decision.
In the subsection “General-Purpose List Implementations,” read the opening comparison of ArrayList and LinkedList. Then continue through the performance warning, which explains why a theoretically favorable linked-list operation does not automatically make LinkedList the faster overall choice.
The lesson here is broader than lists:
Analyze the cost of the operations your algorithm performs most often, not just the name of the data structure.
A supposedly linear algorithm can become quadratic if it repeatedly performs a linear-cost operation. For example, repeatedly calling get(i) on a LinkedList inside an index-based loop can repeatedly traverse nodes. Conversely, an ArrayList may remain the practical choice even when it occasionally shifts elements, because it supports fast positional access and contiguous storage.
The next lesson will treat these Java collection choices systematically. For now, connect the ideas: the hash-map Two Sum solution is faster because its dominant operation—checking whether the complement has been seen—is expected , rather than an scan.
An interview-ready comparison narrative
For the index-returning Two Sum prompt, a concise but substantive explanation might sound like this:
“The brute-force solution checks every pair, so it takes time and auxiliary space. To avoid rescanning previously examined values, I can store each value and its index in a hash map. For each current value, I look up
target - valuebefore inserting the current value, which ensures I use two distinct indices and handles duplicates correctly. Assuming expected constant-time hash-map operations, this takes expected time and space. A sorting and two-pointer alternative would use time, but it either mutates the input or requires a copy, and preserving original indices needs extra bookkeeping. Because this prompt requires indices and does not prohibit additional memory, I would choose the hash map.”
That answer does four things an interviewer needs to hear:
- It establishes a correct baseline.
- It derives the improvement rather than naming it without rationale.
- It reports time and space precisely.
- It justifies the selected approach against the contract.
Key takeaways
When comparing algorithms:
- Compare like for like: use the same input size, output requirements, and complexity case.
- Start with a correct baseline; it gives you a reference point for improvement.
- A hash map can replace repeated searches with expected constant-time lookups, often changing work into expected .
- Sorting plus two pointers is a useful alternative when ordered data is allowed and extra memory is constrained, but it may mutate input and complicate original-index outputs.
- Big O predicts scaling, while memory overhead, cache behavior, mutation, library operation costs, and implementation risk decide the final engineering choice.
- State assumptions explicitly, especially for expected hash-map performance and the behavior of sorting.
Next, you will make these trade-offs more concrete by selecting appropriate Java collection types for interview problems based on their operation costs.
Can't find a good explanation? Sign up and we'll make it for you
Sign up