Need the second smallest and second largest in one array? We compare a clear two-pass “better” method with a one-pass optimal trick. Full intuition, commented code, dry runs, and Big-O analysis.
So let’s get started with the [Problem Link].
1. Problem statement
You are given an array
a
of lengthn
.
Return a list[secondLargest, secondSmallest]
, where
secondLargest
= the second-largest distinct value ina
secondSmallest
= the second-smallest distinct value ina
All elements are distinct, and
n ≥ 2
.
2. Example
a | Answer | Why |
---|---|---|
[5, 1, 3, 4, 2] | [4, 2] | Sorted order → 1 < 2 < 3 < 4 < 5 .Second-largest = 4, second-smallest = 2 |
[9, 7] | [7, 9] | Only two values: second-largest = smaller one (7), second-smallest = larger one (9). |
3. High-level idea
We need the top two and bottom two distinct numbers.
No sorting is required; a couple of variables can track them while we scan the array.
You may be interested in solving “Check if array is sorted or not”.
4. Better solution – two passes (O 2-pass)
4.1 Intuition
- First pass – find the absolute smallest and largest.
- Second pass – find the best remaining candidates that are
- greater than the smallest (→ second-smallest) and
- smaller than the largest (→ second-largest).
4.2 Code
from typing import List
def getSecondOrderElements(n: int, a: List[int]) -> List[int]:
small = float("inf")
second_small = float("inf")
large = float("-inf")
second_large = float("-inf")
# -------- Pass 1 : absolute min & max --------
for i in range(0, len(a)):
small = min(small, a[i])
large = max(large, a[i])
# -------- Pass 2 : second min & second max --------
for i in range(0, len(a)):
if a[i] < second_small and a[i] != small: # candidate > min
second_small = a[i]
if a[i] > second_large and a[i] != large: # candidate < max
second_large = a[i]
return [second_large, second_small]
4.3 Step-by-step explanation
- First loop sets
small
andlarge
. - Second loop revisits each value:
- If it beats the current
second_small
but is not the absolute min → update. - If it beats the current
second_large
but is not the absolute max → update.
- If it beats the current
- Return the two found numbers.
4.4 Dry run (array [5, 1, 3, 4, 2]
)
Pass | Variables after pass |
---|---|
1 | small=1 , large=5 |
2 | second_small=2 , second_large=4 |
Return [4, 2]
.
4.5 Complexity
- Time:
O(n)
+O(n)
=O(2n)
→ O(n) (linear). - Space:
O(1)
.
5. Optimal solution – one pass (O 1-pass)
5.1 Why faster?
We can update both first- and second-rank variables on the same walk with carefully chosen if–elif
ladders.
5.2 Code
from typing import List
def getSecondOrderElements(n: int, a: List[int]) -> List[int]:
small = float("inf")
second_small = float("inf")
large = float("-inf")
second_large = float("-inf")
# -------- Single pass updates everything --------
for i in range(0, len(a)):
# ----- smaller side -----
if a[i] < small: # new absolute min
second_small = small
small = a[i]
elif a[i] < second_small and a[i] != small:
second_small = a[i]
# ----- larger side -----
if a[i] > large: # new absolute max
second_large = large
large = a[i]
elif a[i] > second_large and a[i] != large:
second_large = a[i]
return [second_large, second_small]
5.3 Step-by-step explanation
- Four sentinels start at ±infinity to simplify comparisons.
- For each value
x
:- Left block checks the smaller side.
- If
x
beats the current smallest, slide the oldsmall
down tosecond_small
, then setsmall = x
. - Else if
x
lands strictly betweensmall
andsecond_small
, it becomes the newsecond_small
.
- If
- Right block handles the larger side in the same spirit.
- If
x
exceedslarge
, push the oldlarge
down tosecond_large
, then setlarge = x
. - Else if
x
floats betweensecond_large
andlarge
, it becomes the newsecond_large
.
- If
- Left block checks the smaller side.
Because each block is separate, the same element may update both pairs when the array has only two numbers, exactly what we want.
5.4 Dry run on [9, 7]
x | Updates |
---|---|
9 | Left block → small=9 ; second_small=inf .Right block → large=9 ; second_large=-inf . |
7 | Left block: x < small → second_small=9 , small=7 .Right block: x > second_large and x != large → second_large=7 . |
Return [7, 9]
.
5.5 Complexity
- Time:
O(n)
– single scan. - Space:
O(1)
– constant variables only.
6. Which one to choose?
Criterion | Better (2-pass) | Optimal (1-pass) |
---|---|---|
Code clarity | Slightly simpler mental model | Still clear but denser if–elif logic |
Array read count | 2 × n | 1 × n |
Speed difference | Negligible for small n ; linear either way | Technically half the work |
For interviews, show the better solution first (clean concept), then improve to the optimal one to demonstrate refinement.
7. Key takeaways
- Tracking only two smallest / largest values lets you solve “second-order” problems in constant space.
- A clever ordering of
if–elif
statements collapses two passes into one without hurting readability. - Always reset second variables before the first ones change, otherwise you lose information.
For any changes to the article, kindly email at code@codeanddebug.in or contact us at +91-9712928220.