Advertisement
Home Data Structure Arrays Find the Second Smallest Element in an Array

Find the Second Smallest Element in an Array

1
Advertisement

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:

  1. Validate that the array is non-null and has at least two elements; otherwise throw an exception.
  2. Initialize smallest and secondSmallest to Integer.MAX_VALUE.
  3. Iterate over every element in the array.
  4. If the current element is strictly less than smallest, promote smallest into secondSmallest and set smallest to the current element.
  5. Otherwise, if the current element is strictly less than secondSmallest and not equal to smallest, update secondSmallest only.
  6. After the loop, if secondSmallest is still Integer.MAX_VALUE, no distinct runner-up exists; throw an exception.
  7. Return secondSmallest.

Java Program

Here is the complete, compilable program that implements the approach above.

Java
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.

Iterationnumbers[i]smallestsecondSmallestAction
177MAX_VALUEnew smallest
2337new smallest
3737skip, not smaller than secondSmallest
4113new smallest
5212new secondSmallest
6112skip, 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

Java
int[] numbers = {12, 35, 1, 10, 34, 1};
int secondSmallest = findSecondSmallest(numbers);
System.out.println(secondSmallest);
// Output: 10

The 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

Java
int[] numbers = {-5, -2, -10, -1, -8};
int secondSmallest = findSecondSmallest(numbers);
System.out.println(secondSmallest);
// Output: -8

An 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

Java
int[] numbers = {5, 5, 20, 40, 5};
int secondSmallest = findSecondSmallest(numbers);
System.out.println(secondSmallest);
// Output: 20

Here 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) when O(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 0 is wrongly reported as a candidate minimum. Initialize both trackers to Integer.MAX_VALUE.
  • Dropping the != smallest guard – lets a duplicate of the current minimum overwrite the runner-up, so {5, 5, 20} returns 5 instead of 20. 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 leaves secondSmallest equal to smallest. Use strict less-than so duplicates fall through to the else-if.
  • Silently returning Integer.MAX_VALUE for all-equal arrays – the caller cannot distinguish a genuine answer from the sentinel. Throw IllegalArgumentException so 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 != smallest guard; 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 typically O(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.