DEV Community

Cover image for Solution: Set Mismatch (ver. 2)
seanpgallivan
seanpgallivan

Posted on

Solution: Set Mismatch (ver. 2)

This is part of a series of Leetcode solution explanations (index). If you liked this solution or found it useful, please like this post and/or upvote my solution post on Leetcode's forums.


Note: This is my second version of a solution for this problem. Though I consider the first version a less complex solution more appropriate for an "Easy" problem, this post demonstrates how to achieve the solution with a space complexity of only O(1) extra space instead of O(N).


Leetcode Problem #645 (Easy): Set Mismatch


Description:


(Jump to: Solution Idea || Code: JavaScript | Python | Java | C++)

You have a set of integers s, which originally contains all the numbers from 1 to n. Unfortunately, due to some error, one of the numbers in s got duplicated to another number in the set, which results in repetition of one number and loss of another number.

You are given an integer array nums representing the data status of this set after the error.

Find the number that occurs twice and the number that is missing and return them in the form of an array.


Examples:

Example 1:
Input: nums = [1,2,2,4]
Output: [2,3]
Example 2:
Input: nums = [1,1]
Output: [1,2]

Constraints:

  • 2 <= nums.length <= 10^4
  • 1 <= nums[i] <= 10^4

Idea:


(Jump to: Problem Description || Code: JavaScript | Python | Java | C++)

In order to solve this problem with O(1) extra space, we can use nums directly to keep track of which numbers have been seen so far. To do so, we need to be able to modify the elements of nums in such a way as to be easily able to obtain the original value again.

One of the easiest ways to do this is with the use of the mod operator (%). Since the largest value nums[i] is 10^4, we can use that number as our base. By adding 10^4 to the value of an element, it can now tell us two things: the original value of the element (num % 10^4) and whether or not the number equal to the index has been seen (num > 10^4).

Since the values in nums are 1-indexed and nums itself is 0-indexed, however, we'll have to shift the mod function to (nums - 1) % 10^4.

If we iterate through nums and apply this addition, then at the end, we'll know that the value that was seen twice will be > 20000 and the number that was never seen is < 10001.

So we just have to iterate through nums a second time, check for these values, add them to our answer (ans), and then return ans.


Implementation:

There are very few differences between the four languages for this solution.


Javascript Code:


(Jump to: Problem Description || Solution Idea)

var findErrorNums = function(nums) {
    let N = nums.length, ans = [,]
    for (let i = 0; i < N; i++)
        nums[(nums[i] - 1) % 10000] += 10000
    for (let i = 0; i < N; i++)
        if (nums[i] > 20000) ans[0] = i + 1
        else if (nums[i] < 10001) ans[1] = i + 1
    return ans
};
Enter fullscreen mode Exit fullscreen mode

Python Code:


(Jump to: Problem Description || Solution Idea)

class Solution:
    def findErrorNums(self, nums):
        ans = [0,0]
        for num in nums:
            nums[(num - 1) % 10000] += 10000
        for i in range(len(nums)):
            if nums[i] > 20000: ans[0] = i + 1
            elif nums[i] < 10001: ans[1] = i + 1
        return ans
Enter fullscreen mode Exit fullscreen mode

Java Code:


(Jump to: Problem Description || Solution Idea)

class Solution {
    public int[] findErrorNums(int[] nums) {
        int N = nums.length;
        int[] ans = new int[2];
        for (int num : nums)
            nums[(num - 1) % 10000] += 10000;
        for (int i = 0; i < N; i++)
            if (nums[i] > 20000) ans[0] = i + 1;
            else if (nums[i] < 10001) ans[1] = i + 1;
        return ans;
    }
}
Enter fullscreen mode Exit fullscreen mode

C++ Code:


(Jump to: Problem Description || Solution Idea)

class Solution {
public:
    vector<int> findErrorNums(vector<int>& nums) {
        int N = nums.size();
        vector<int> ans(2);
        for (int num : nums)
            nums[(num - 1) % 10000] += 10000;
        for (int i = 0; i < N; i++)
            if (nums[i] > 20000) ans[0] = i + 1;
            else if (nums[i] < 10001) ans[1] = i + 1;
        return ans;
    }
};
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
grahamthedev profile image
GrahamTheDev • Edited

Interesting, but given the constraints isn't this as simple as just looping through an ordered array until we get a number that isn't it's index + 1 and then checking if the before value is the same? As there can only ever be one digit incorrect and it must be one higher or one lower than the digit that is replaced?

Or did I miss some key part of the question as I am terrible at these sorts of things lol!

If I am correct then the beauty of this is a single loop that exits the second it finds a match so it should be computationally efficient.

Collapse
 
seanpgallivan profile image
seanpgallivan

But you're assuming that the input array (nums) is sorted; the problem description doesn't state that. And sorting it first would make the solution O(N * log N) instead of just O(N).

It can be a bit hard to explain, but each element of the solution posted here tells us two things... but about different numbers. One number is the actual value of the element, the other is the number that is equal to the index of the element plus 1.

So if we reach the end, for example, and see that nums[5] = 20002, then we know that the number 6 (i = 5 so i + 1 = 6) was seen twice, not the number 2. And yet we still preserved our ability to read the number 2 in this element with 20002 % 10000.

That's important because we'll be modifying the elements out of order in our first pass, and don't want to screw up our ability to properly read each number later on in the pass.

So in other words, in the first pass we're reading nums at i, but we're modifying nums at nums[i]. That's because we're treating nums very much like we would its own "seen" or "visited" array. We just don't want those two purposes for nums to impact each other, so we just... overlay another level of meaning onto the values stored in nums.

It's very much similar to using bit manipulation to combine two pieces of data into one.

Collapse
 
grahamthedev profile image
GrahamTheDev

This is why I am rubbish at these problems, I forget that .sort is expensive!

I get it entirely now and thanks for taking the time to give such a detailed explanation!

Thread Thread
 
seanpgallivan profile image
seanpgallivan

No worries, and thanks for the question!