Back to Arrays
Q14Question 14 of 109

Find the Largest and Smallest Element in an Array

Key Concepts
  • Seed both max and min with the first element, then scan once, updating each when you see something bigger or smaller.
  • A single pass gives both answers in O(n) time and O(1) space, which is strictly better than sorting at O(n log n).
  • Seed with the first real element, not zero, because amounts can be negative debits.
  • For money use BigDecimal and compareTo for correct value comparison.
  • The pairwise optimisation reduces comparisons from 2n to 1.5n by processing elements in pairs.
  • Return both results together in one pass rather than scanning the array twice.

Definition

Seed max and min with the first element, then scan once, updating each when you see something bigger or smaller.
  • Single pass – iterate once from left to right, tracking both the maximum and minimum simultaneously.
  • Two trackers – initialise both max and min with the first element of the array.
  • O(n) time – each element is visited once, with two comparisons per element.
  • O(1) space – only two tracking variables are needed.
  • Better than sorting – sorting costs O(n log n) and is overkill when only two values are needed.

The fraud desk at National Bank of India wants both the biggest and the smallest transaction on Rajesh's account today. Instead of sorting the whole day's list, a clerk simply walks down the list once, remembering the biggest seen so far and the smallest seen so far, updating both as new amounts appear. By the end of one pass, the clerk has both answers.

Understanding the Concepts

What is the problem?

Given an array, find both the largest and smallest elements. In a banking context, the fraud desk at National Bank of India wants both the biggest transaction (possible large unauthorised transfer) and the smallest transaction (possible test transaction before fraud) on Rajesh's account today.

How does the single-pass algorithm work?

Guard the empty case first. For a non-empty array, initialise both max and min to the first element. Then loop from the second element to the end, comparing each element against both trackers.

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class FraudScreener {

    public static BigDecimal[] findMinMax(BigDecimal[] amounts) {
        if (amounts == null || amounts.length == 0) {
            throw new IllegalArgumentException("No transactions to analyze");
        }
        BigDecimal max = amounts[0];
        BigDecimal min = amounts[0];
        for (int i = 1; i < amounts.length; i++) {
            if (amounts[i].compareTo(max) > 0) {
                max = amounts[i];
            }
            if (amounts[i].compareTo(min) < 0) {
                min = amounts[i];
            }
        }
        return new BigDecimal[]{ min, max };
    }

    public static void main(String[] args) {
        BigDecimal[] dayTxns = {
            new BigDecimal("15000.00"),
            new BigDecimal("42500.75"),
            new BigDecimal("1.00"),
            new BigDecimal("67200.50"),
            new BigDecimal("3100.25")
        };
        BigDecimal[] result = findMinMax(dayTxns);
        System.out.println("Smallest: Rs." + result[0]);
        System.out.println("Largest:  Rs." + result[1]);
    }
}
TEXT
Smallest: Rs.1.00
Largest:  Rs.67200.50

Why seed with the first element?

Seeding with zero fails when all amounts are negative (debit-only view). Seeding with Integer.MIN_VALUE and Integer.MAX_VALUE does not apply to BigDecimal, which has no such constants. The first real element is always a safe seed.

Why is one pass better than sorting?

Sorting the array and picking the first and last elements works, but costs O(n log n). The single-pass approach is O(n), which is strictly faster. It also does not modify the array, while sorting would reorder it.

What is the pairwise comparison optimisation?

The naive approach does two comparisons per element (against max and against min), totalling 2(n-1) comparisons. The pairwise technique processes elements in pairs: compare the pair with each other, then compare the smaller against min and the larger against max. This does 3 comparisons per 2 elements, reducing the total to roughly 1.5n.

JAVA
// Pairwise optimisation sketch
for (int i = 1; i < amounts.length - 1; i += 2) {
    BigDecimal smaller, larger;
    if (amounts[i].compareTo(amounts[i + 1]) < 0) {
        smaller = amounts[i];
        larger = amounts[i + 1];
    } else {
        smaller = amounts[i + 1];
        larger = amounts[i];
    }
    if (smaller.compareTo(min) < 0) min = smaller;
    if (larger.compareTo(max) > 0) max = larger;
}
// Handle last element if array length is even

What about the empty array?

An empty array has no largest or smallest. Returning zeros would mislead the fraud desk. Throwing IllegalArgumentException makes the absence explicit and forces the caller to handle it.

What is the time and space complexity?

  • Time: O(n) because each element is visited once.
  • Space: O(1) because only two tracking variables are used.
  • The pairwise optimisation reduces the constant factor but not the big-O class.

What are common mistakes?

The most common mistake is scanning the array twice, once for max and once for min, which doubles the work unnecessarily. Another mistake is seeding min with zero and max with zero, which fails when all values are negative or all are larger than zero.

JAVA
// Wrong: two passes
BigDecimal max = findMax(amounts); // O(n)
BigDecimal min = findMin(amounts); // O(n) again, total 2n

// Correct: one pass
// Track both in the same loop as shown above

What should a senior engineer know?

A senior engineer returns both results together in an immutable result object or a two-element array rather than making the caller call two separate methods. For very large distributed datasets, the scan parallelises trivially because each partition finds its local min and max, and the global min/max is the min of local mins and max of local maxes. In the fraud screening pipeline at National Bank of India, this partition-and-merge strategy runs across multiple threads on a day's worth of millions of transactions.

Visual Overview

DIAGRAM
  amounts[]:  15000  42500  1.00  67200  3100
                │
                ▼
  max = 15000, min = 15000  (seed with first)
  ┌──────────────────────────────────────────────────┐
  │  i=1: 42500 > 15000? YES → max=42500             │
  │       42500 < 15000? NO                           │
  │  i=2: 1.00  > 42500? NO                           │
  │       1.00  < 15000? YES → min=1.00               │
  │  i=3: 67200 > 42500? YES → max=67200              │
  │       67200 < 1.00?  NO                           │
  │  i=4: 3100  > 67200? NO                           │
  │       3100  < 1.00?  NO                           │
  └──────────────────────────────────────────────────┘
  Result: min=1.00, max=67200.50

Key Takeaway

Finding the largest and smallest elements of an array is a single linear pass. I seed both a maximum and a minimum tracker with the first element, then walk the rest of the array once, updating the maximum when I see a larger value and the minimum when I see a smaller value. One pass gives both answers in linear time with constant extra space, which beats sorting.

In my banking project at National Bank of India, I used this on the fraud desk, where the biggest transaction of the day is a red flag for an unusually large transfer and the smallest can be a tiny test transaction that often precedes fraud. Scanning the day's amounts once gives both, exactly, in BigDecimal.

I mention the pairwise optimisation that cuts comparisons by about a quarter by comparing elements in pairs first, though I note the simple version is clearer. I always return both results together so the caller gets them from a single scan.

Remember one sentence: seed both trackers with the first element, scan once, and update each when you find something bigger or smaller.

---

Share
Use ← → arrow keys to navigate