Back to HashMap & HashSet Problems
Q14Question 14 of 17

Find the Corrupt Pair (One Duplicate and One Missing in 1..n)

Key Concepts
  • After Cyclic Sort placement, exactly one slot has value != index+1; its stored value is the duplicate and index+1 is the missing number.
  • The duplicate-safe value-comparison guard prevents infinite swaps when the duplicate reaches its home.
  • One placement pass plus one scan: O(n) time, O(1) extra space.
  • The algebraic alternative uses sum and sum-of-squares differences to solve two equations for two unknowns, but risks overflow for large n.
  • Cyclic Sort is overflow-free and more readable, making it the preferred interview answer.
  • This problem combines Find the Duplicate and Find the Missing into a single unified Cyclic Sort application.

Definition

After cyclic placement, the one mismatched slot gives the duplicate (its value) and the missing (its index+1) together.
  • Corrupt Pair – an array of n values in 1..n has one duplicate and one missing; return both.
  • Cyclic Sort – place each value v at index v-1; after placement, exactly one slot is mismatched.
  • Mismatched slot – the stored value is the duplicate, and index+1 is the missing number.
  • Algebraic alternative – use the difference of sums and squared sums to solve for both, but risks overflow.
  • Complexity – O(n) time, O(1) extra space.

The National Bank of India keeps one ledger page per number 1 to n, but a copying error duplicated one page number and therefore dropped another. A clerk files each page at its slot. The single stranded slot tells both the page number that was copied twice and the page number that vanished.

Understanding the Concepts

What is the Corrupt Pair problem?

An array of n numbers should hold each value from 1 to n exactly once, but due to corruption one number appears twice and another is missing. The task is to return both the duplicate and the missing number in O(n) time and O(1) extra space.

JAVA
package com.bank.dsa;

public class CorruptPairFinder {
    // Returns [duplicate, missing]
    public int[] findCorruptPair(int[] nums) {
        // implementation below
        return new int[]{};
    }
}

How does Cyclic Sort solve this?

Run the Cyclic Sort placement loop with the duplicate-safe value-comparison guard. Because exactly one value is duplicated and exactly one is missing, precisely one slot ends up with nums[i] != i + 1 after placement. At that slot, nums[i] is the duplicate and i + 1 is the missing number.

JAVA
package com.bank.dsa;

public class LedgerAuditor {
    public static int[] findCorruptPair(int[] ledger) {
        // Cyclic Sort placement
        int i = 0;
        while (i < ledger.length) {
            int home = ledger[i] - 1;
            if (ledger[i] != ledger[home]) {
                int temp = ledger[i];
                ledger[i] = ledger[home];
                ledger[home] = temp;
            } else {
                i++;
            }
        }

        // Find the mismatched slot
        for (int j = 0; j < ledger.length; j++) {
            if (ledger[j] != j + 1) {
                return new int[]{ledger[j], j + 1}; // [duplicate, missing]
            }
        }
        return new int[]{};
    }

    public static void main(String[] args) {
        int[] ledger = {3, 1, 2, 5, 2};
        int[] result = findCorruptPair(ledger);
        System.out.println("Duplicate: " + result[0] + ", Missing: " + result[1]);
    }
}
TEXT
Duplicate: 2, Missing: 4

Why does exactly one slot end up mismatched?

The duplicate value occupies two slots in the input. During placement, the first copy goes to its home index. The second copy finds its home already occupied by the first copy (the value-comparison guard fires), so it stays stranded. The missing value's home slot is therefore occupied by the stranded duplicate, giving us both answers from one slot.

TEXT
Input:  [3, 1, 2, 5, 2]  (n=5, duplicate=2, missing=4)
After:  [1, 2, 3, 2, 5]
                   ^--- slot 3: value=2, expected=4
                        duplicate=2, missing=4

How does the algebraic alternative work?

Compute the expected sum (1+2+...+n = n*(n+1)/2) and the actual sum. The difference gives duplicate - missing. Similarly, compute the expected and actual sums of squares. The difference of squared sums gives duplicate^2 - missing^2 = (duplicate + missing)(duplicate - missing). With both equations you can solve for both unknowns.

JAVA
package com.bank.dsa;

public class AlgebraicCorruptPair {
    public static int[] findCorruptPair(int[] nums) {
        long n = nums.length;
        long expectedSum = n * (n + 1) / 2;
        long expectedSqSum = n * (n + 1) * (2 * n + 1) / 6;

        long actualSum = 0, actualSqSum = 0;
        for (int num : nums) {
            actualSum += num;
            actualSqSum += (long) num * num;
        }

        long diff = actualSum - expectedSum;       // dup - miss
        long sqDiff = actualSqSum - expectedSqSum; // dup^2 - miss^2
        long sumDupMiss = sqDiff / diff;            // dup + miss

        int duplicate = (int) ((diff + sumDupMiss) / 2);
        int missing = (int) ((sumDupMiss - diff) / 2);
        return new int[]{duplicate, missing};
    }
}

Why prefer Cyclic Sort over the algebraic approach?

Cyclic Sort is overflow-free because it only uses array swaps and index comparisons, never computing large sums. The algebraic approach requires sum and sum-of-squares computations that can overflow for large n even with long arithmetic. Cyclic Sort also reads more naturally, making it easier to explain under interview pressure.

| Approach | Overflow Risk | Readability | Time | Space |
|---|---|---|---|---|
| Cyclic Sort | None | High | O(n) | O(1) |
| Algebraic | Yes (large n) | Moderate | O(n) | O(1) |
| XOR-based | None | Low | O(n) | O(1) |

What are edge cases?

Edge cases include the duplicate being 1 (the smallest value), the duplicate being n (the largest value), and the missing being 1 or n. All cases result in exactly one mismatched slot after placement.

JAVA
findCorruptPair(new int[]{1, 1});       // [1, 2]
findCorruptPair(new int[]{2, 2});       // [2, 1]
findCorruptPair(new int[]{2, 3, 3});    // [3, 1]

What are common mistakes?

The most common mistake is returning i + 1 as the duplicate and nums[i] as the missing, which swaps the two answers. Remember: the stored value is the duplicate (it is the extra copy), and the index+1 is the missing (the slot that was supposed to hold it).

JAVA
// Wrong: swapped
return new int[]{j + 1, ledger[j]}; // [missing, duplicate]

// Correct:
return new int[]{ledger[j], j + 1}; // [duplicate, missing]

What should a senior engineer know?

A senior engineer recognises this as the natural combination of Find the Duplicate and Find the Missing in a single Cyclic Sort pass. They present both the Cyclic Sort and algebraic approaches, explain the overflow trade-off, and pick Cyclic Sort for interviews. They also mention the XOR-based approach as a third option that is overflow-free but harder to explain.

Visual Overview

DIAGRAM
  Input: [3, 1, 2, 5, 2]  (n=5)

  Cyclic Sort Placement:
  i=0: nums[0]=3, home=2, nums[2]=2 != 3 --> swap
       [2, 1, 3, 5, 2]
  i=0: nums[0]=2, home=1, nums[1]=1 != 2 --> swap
       [1, 2, 3, 5, 2]
  i=0: nums[0]=1, home=0, 1==1 --> i++
  i=1: nums[1]=2, 2==2 --> i++
  i=2: nums[2]=3, 3==3 --> i++
  i=3: nums[3]=5, home=4, nums[4]=2 != 5 --> swap
       [1, 2, 3, 2, 5]
  i=3: nums[3]=2, home=1, nums[1]=2 == 2 --> i++
  i=4: nums[4]=5, 5==5 --> i++

  After: [1, 2, 3, 2, 5]
                   ^--- slot 3: value=2 != 4

  Result: duplicate = 2, missing = 4

  ┌────────────────────────────────────┐
  │ Mismatched slot:                   │
  │   stored value  = duplicate (2)    │
  │   index + 1     = missing   (4)    │
  └────────────────────────────────────┘

Key Takeaway

Finding the corrupt pair, where one number is duplicated and that duplication forced another number to go missing, is one of the most satisfying Cyclic Sort applications because a single placement pass surrenders both answers at once. I place each value at the index equal to the value minus one with the duplicate-safe guard, and because exactly one value is doubled and exactly one is absent, precisely one slot ends up mismatched. At that slot the stored value is the duplicate and the index plus one is the missing number.

I know the algebraic approach using the difference of sums and squared sums also recovers both unknowns, but it risks overflow for large n and reads less clearly, so Cyclic Sort is my preferred interview answer. The XOR-based approach is a third option that avoids overflow but is the hardest to explain.

This problem is the natural combination of Find the Duplicate and Find the Missing into a single unified Cyclic Sort application. Recognising that unification is what makes it elegant and quick to code.

In my work at the National Bank of India, this backed a ledger integrity audit. The ledger assigned each entry a page number in a known range, and a copy fault occasionally duplicated one page number while dropping another. Placing every page at its home index then reporting the single mismatched slot delivered both numbers in linear time and constant space.

Remember one sentence: after Cyclic Sort, the one mismatched slot gives both the duplicate (its stored value) and the missing (its index+1) in a single O(n) pass.

---

Share
Use ← → arrow keys to navigate