I was reviewing for an interview and encountered the following question (and I'm paraphrasing):
Suppose you're given a shuffled array of numbers containing all of the numbers between a min and a max, inclusive, with one number missing. For example, for an array of size 5, you could have something like 2, 5, 4, 1 (where 3 is missing).
How do you determine which one is missing?
When I was posting, I did discover a similar post here (that OP actually settled on a similar preferred solution as I did, but I came up with mine independently), but I have several other solutions as well (as well as a slightly different implementation).
I thought of a few possible solutions: the first (which I think is actually a bad solution) would be to sort the list and then look for which one is missing.
A really bad solution would be to try every possible number between the min and max (does the list contain 1? Does the list contain 2? Does the list contain 3? Etc.).
There are two good solutions that I thought of. First (which I think is the inferior of the two) uses a hash table - essentially, I "check off" every number in the range as I see it in the array I got and then I look to see which one is still false. It's below:
private static int MissingNumber2(int[] numbers, int min, int max)
{
var dictionary = new Dictionary<int, bool>();
for (int i = min; i <= max; i++)
{
dictionary[i] = false;
}
foreach (int number in numbers)
{
dictionary[number] = true;
}
return dictionary.Keys.First(key => !dictionary[key]);
}
The simplest solution I came up with is to sum the items in the range and then sum the items in the actual array; the difference between them is the number that's missing. Here's that code:
/// <summary>
/// Find the missing number in a randomly-sorted array
/// </summary>
/// <param name="numbers">Array of random numbers between <paramref name="min"/> and <paramref name="max"/> (inclusive) with one number missing</param>
/// <param name="min">Minimum number (inclusive)</param>
/// <param name="max">Maximum number (inclusive)</param>
/// <returns>Missing number</returns>
private static int MissingNumber(int[] numbers, int min, int max)
{
int expectedSum = 0;
int actualSum = 0;
// Eventually we could cache this if we use the results a lot
for (int i = min; i <= max; i++)
{
expectedSum += i;
}
foreach (int number in numbers)
{
actualSum += number;
}
// I do realize I could just return this directly but this is slightly more convenient for debugging
int missingNumber = expectedSum - actualSum;
return missingNumber;
}
Next, for completeness, here's the code I use to generate the random arrays in the first place:
/// <summary>
/// Generate an array with all of the numbers except 1 between <paramref name="min"/> and <paramref name="max"/> in random order
/// </summary>
/// <param name="min">Smallest number in the array (inclusive)</param>
/// <param name="max">Largest number in the array (inclusive)</param>
/// <returns>Array</returns>
/// <example>
/// If min = 1 and max = 5, then we start with the array 1, 2, 3, 4, 5.
///
/// We then remove a random number (say, for example, 2), which leaves us with 1, 3, 4, 5.
///
/// We then shuffle the array by swapping random indices and return the result - e.g. 4, 1, 5, 3.
/// </example>
private static int[] GenerateRandomArray(int min, int max)
{
List<int> array = new List<int>();
for (int i = min; i <= max; i++)
{
array.Add(i);
}
Random random = new Random();
// Now we shuffle the array by swapping two random indices
// The maximum number for the loop is somewhat arbitrary, but we want it to be at
// least as large as array.Count so that we can make sure that the array's
// thoroughly shuffled
for (int i = 0; i < random.Next(array.Count * 3); i++)
{
int j = random.Next(array.Count);
int k = random.Next(array.Count);
// If j == k we're not actually doing a swap
// so generate a new k until we get something other than j
while (j == k)
{
k = random.Next(array.Count);
}
int temp = array[j];
array[j] = array[k];
array[k] = temp;
}
// Remove an item at a random index
array.RemoveAt(random.Next(array.Count));
return array.ToArray();
}
Here's an example of the calls:
int[] randomArray = GenerateRandomArray(1, 10);
int missingNumber = MissingNumber(randomArray, 1, 10);
// For verification purposes - it's easier to see the missing number if the array's sorted
int[] sortedArray = randomArray.OrderBy(i => i).ToArray();
int missingNumber2 = MissingNumber2(randomArray, 1, 10);
Console.WriteLine("Missing number: " + missingNumber.ToString());