Title: Find the Second Smallest Element in an Array
In this guide, you’ll learn how to find the second smallest element in an array using a single pass through the data. We’ll frame the problem, build a clean linear-time solution in Java, trace it by hand on a fresh input, and then stress test it against the edge cases that catch candidates off guard in coding interviews.
The task looks small, but it hides a few sharp corners around duplicates, positive-only arrays, and very short inputs. By the end of this article you’ll have a program you can defend under follow-up questions and a checklist for the boundary inputs every interviewer tests.
Understanding the Problem
Given an integer array, we want to return the second smallest distinct value. For {4, 2, 4, 7, 9} the smallest is 2 and the second smallest is 4, not another 2. If every element is identical, no second smallest exists and our program should raise an error rather than quietly return a misleading value.
Two observations drive the solution. First, a single scan is enough: we only need to remember two numbers at any time, the current smallest and the current secondSmallest. Second, we must treat duplicates of the current minimum as already seen so they do not slip into the runner-up slot.
Approach to Find the Second Smallest Element
We can solve this in one linear pass by maintaining two running values. The algorithm steps are:
- Validate that the array is non-null and has at least two elements; otherwise throw an exception.
- Initialize
smallestandsecondSmallesttoInteger.MAX_VALUE. - Iterate over every element in the array.
- If the current element is strictly less than
smallest, promotesmallestintosecondSmallestand setsmallestto the current element. - Otherwise, if the current element is strictly less than
secondSmallestand not equal tosmallest, updatesecondSmallestonly. - After the loop, if
secondSmallestis stillInteger.MAX_VALUE, no distinct runner-up exists; throw an exception. - Return
secondSmallest.
Java Program
Here is the complete, compilable program that implements the approach above.
public class SecondSmallestElement {
public static int findSecondSmallest(int[] numbers) {
if (numbers == null || numbers.length < 2) {
throw new IllegalArgumentException("Array must contain at least two elements");
}
int smallest = Integer.MAX_VALUE;
int secondSmallest = Integer.MAX_VALUE;
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] < smallest) {
secondSmallest = smallest;
smallest = numbers[i];
} else if (numbers[i] < secondSmallest && numbers[i] != smallest) {
secondSmallest = numbers[i];
}
}
if (secondSmallest == Integer.MAX_VALUE) {
throw new IllegalArgumentException("No distinct second smallest element exists");
}
return secondSmallest;
}
public static void main(String[] args) {
int[] numbers = {12, 35, 1, 10, 34, 1};
int secondSmallest = findSecondSmallest(numbers);
System.out.println("The second smallest element is: " + secondSmallest);
}
}Explanation of the Program
The guard clause at the top of findSecondSmallest rejects a null array or one with fewer than two elements, because the problem is undefined for those inputs. Failing fast with IllegalArgumentException is better than returning a sentinel that a caller might silently propagate.
We seed both smallest and secondSmallest with Integer.MAX_VALUE so that any real integer in the array will undercut the initial values. This matters when the array contains only positive numbers, where seeding with 0 would quietly return the wrong answer.
Inside the loop, the first branch handles a new minimum. The previous minimum slides into the second slot before the current element takes the top, so no information is lost. The second branch is the subtle one: the check numbers[i] != smallest is what makes the output the second smallest distinct value. Drop that guard and a tie with the current minimum would overwrite the runner-up and corrupt the answer.
The final guard catches arrays that have a minimum but no distinct runner-up, like {4, 4, 4}. Rather than returning Integer.MAX_VALUE, which the caller might mistake for a real value, we surface the issue at the call site.
Step-by-Step Trace
Let’s run the algorithm on the input {7, 3, 7, 1, 2, 1}, which should finish with smallest = 1 and secondSmallest = 2. Each row below shows the state after that iteration completes.
| Iteration | numbers[i] | smallest | secondSmallest | Action |
|---|---|---|---|---|
| 1 | 7 | 7 | MAX_VALUE | new smallest |
| 2 | 3 | 3 | 7 | new smallest |
| 3 | 7 | 3 | 7 | skip, not smaller than secondSmallest |
| 4 | 1 | 1 | 3 | new smallest |
| 5 | 2 | 1 | 2 | new secondSmallest |
| 6 | 1 | 1 | 2 | skip, equals smallest |
Invariant: at the end of each iteration, smallest holds the minimum of the elements seen so far and secondSmallest holds the next-smallest distinct value seen so far. Iteration 5 is the one worth studying: the current element 2 never challenges smallest, so only the else-if branch fires. Iteration 6 is the other interesting case: a duplicate of the current minimum would corrupt secondSmallest without the != smallest guard.
Examples
The following examples show the program behaving on three different shapes of input.
Example 1: Mixed Positive Integers
int[] numbers = {12, 35, 1, 10, 34, 1};
int secondSmallest = findSecondSmallest(numbers);
System.out.println(secondSmallest);
// Output: 10The minimum 1 appears twice, so the != smallest guard keeps the duplicate from overwriting the runner-up. The next-smallest distinct value is 10, which is what the program returns.
Example 2: Array of Negative Numbers
int[] numbers = {-5, -2, -10, -1, -8};
int secondSmallest = findSecondSmallest(numbers);
System.out.println(secondSmallest);
// Output: -8An all-negative array stresses the algorithm in a different way: -10 wins the smallest slot and -8 becomes the runner-up. Note that “second smallest” means “second from the bottom,” not “second closest to zero.”
Example 3: Repeated Smallest Element
int[] numbers = {5, 5, 20, 40, 5};
int secondSmallest = findSecondSmallest(numbers);
System.out.println(secondSmallest);
// Output: 20Here the minimum 5 appears three times. The != smallest guard keeps those duplicates from overwriting the runner-up, so secondSmallest settles at 20 rather than at 5.
Common Mistakes to Avoid
These are the specific pitfalls candidates run into while coding this problem under pressure.
- Sorting the whole array – solves the problem in
O(n log n)whenO(n)is possible, and interviewers notice. Keep two running trackers and make a single pass instead. - Initializing to zero – fails on arrays containing only positive numbers because
0is wrongly reported as a candidate minimum. Initialize both trackers toInteger.MAX_VALUE. - Dropping the
!= smallestguard – lets a duplicate of the current minimum overwrite the runner-up, so{5, 5, 20}returns5instead of20. Keep the distinctness check in the else-if branch. - Using
<=instead of<on the first branch – demotes the current smallest into the runner-up on every tie, which is wasted work and leavessecondSmallestequal tosmallest. Use strict less-than so duplicates fall through to the else-if. - Silently returning
Integer.MAX_VALUEfor all-equal arrays – the caller cannot distinguish a genuine answer from the sentinel. ThrowIllegalArgumentExceptionso the problem surfaces at the call site.
Edge Cases to Test
Walk through these boundary inputs before claiming the solution is complete. The order below mirrors how often each case shows up in interview grading rubrics.
- Empty array
- Single-element array
- Two-element array with equal values
- All identical elements
- All positive elements
- All negative elements
- Smallest element at index 0
- Smallest element at the last index
- Multiple duplicates of the minimum
- Integer.MAX_VALUE present in the array
- Null array reference
Additional Considerations
The algorithm runs in O(n) time because it performs a single pass over the array and does a constant amount of work per element. Space usage is O(1): only two integer trackers plus the loop index, independent of input size.
A sort-based solution works too but costs O(n log n) time and either O(1) or O(n) space depending on the sort, and it mutates the input unless you copy it first. A heap-based approach using a PriorityQueue of size two is also possible but adds log-factor overhead for no real benefit on this problem.
One edge case worth calling out explicitly: an array that already contains Integer.MAX_VALUE. Our final guard checks secondSmallest == Integer.MAX_VALUE to detect the no-distinct-runner-up case, which collides with a legitimate Integer.MAX_VALUE input value. In a production setting you would use a boolean flag or an OptionalInt return type to disambiguate; for interview purposes it is enough to mention the collision aloud.
Interview Follow-up Questions
Interviewers almost always probe deeper after the base solution works. Expect variants along these lines.
- What if duplicates of the minimum should count as the second smallest? – drop the
!= smallestguard; the algorithm then returns the second element by position in the sorted order, allowing ties. - What if the input is already sorted in ascending order? – scan from the start for the first value strictly greater than the first element; the running trackers are unnecessary and the walk is
O(n)worst case but typicallyO(1). - What if you need the K-th smallest rather than the second smallest? – maintain a max-heap of size K, pushing each element and evicting the largest; the final heap root is the answer in
O(n log K). - What if the array arrives as an unbounded stream? – keep the same two trackers and update them per incoming value; termination comes from end-of-stream rather than array length, and memory stays
O(1). - What if the array is too large to fit in memory? – partition the input into chunks, compute the bottom two values per chunk independently, then merge the per-chunk pairs with the same algorithm; the chunk step is embarrassingly parallel.
Conclusion
Finding the second smallest element rewards a careful single-pass algorithm over a sort-and-slice shortcut. Two trackers, a strict less-than check, and a distinctness guard turn a deceptively simple problem into a clean O(n) solution that handles positives, duplicates, and short arrays without surprises.
Try adapting the program to return the bottom K distinct values, or to work on a List<Integer> rather than a primitive array. You can learn more about array problems like this one on our Arrays category page.



