Here is an algorithm that checks for a single duplicate in \$O(\log n)\$ time. It performs a binary search on the sorted sequence. The binary search is driven by comparing the array index with the array element at that index. If the two integers match, then no duplicate can exist with a lower array index. Of course, it is very sensitive to the assumptions you presented in your question.
Here are test cases to validate the scenario:
[TestMethod]
public void DuplicateCheck()
{
// Assumptions:
// - The array is sorted in ascending order.
// - The array is only populated with integers starting with 0
// with no integer values skipped.
// - There is only one duplicate.
Assert.AreEqual(9, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9 }));
Assert.AreEqual(-1, CheckForSingleDuplicateInSequence(new int[] { }));
Assert.AreEqual(-1, CheckForSingleDuplicateInSequence(new int[] { 0 }));
Assert.AreEqual(-1, CheckForSingleDuplicateInSequence(new int[] { 0, 1 }));
Assert.AreEqual(1, CheckForSingleDuplicateInSequence(new int[] { 0, 0 }));
Assert.AreEqual(-1, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2 }));
Assert.AreEqual(1, CheckForSingleDuplicateInSequence(new int[] { 0, 0, 1 }));
Assert.AreEqual(2, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 1 }));
Assert.AreEqual(-1, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2, 3 }));
Assert.AreEqual(1, CheckForSingleDuplicateInSequence(new int[] { 0, 0, 1, 2 }));
Assert.AreEqual(2, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 1, 2 }));
Assert.AreEqual(3, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2, 2 }));
Assert.AreEqual(-1, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2, 3, 4, 5, 6, 7 }));
Assert.AreEqual(3, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2, 2, 3, 4, 5, 6 }));
Assert.AreEqual(7, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2, 3, 4, 5, 6, 6 }));
Assert.AreEqual(-1, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }));
Assert.AreEqual(9, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }));
Assert.AreEqual(23, CheckForSingleDuplicateInSequence(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 22, 23 }));
}
Here is the function:
private static int CheckForSingleDuplicateInSequence(int[] input)
{
if (input == null)
{
throw new System.ArgumentNullException("input");
}
if (input.Length < 2)
{
return -1;
}
else if (input.Length == 2)
{
return input[0] == input[1] ? 1 : -1;
}
int leftIndex = 0;
int rightIndex = input.Length - 1;
int midIndex = (int)(((uint)leftIndex + (uint)rightIndex) >> 1);
do
{
if (input[midIndex] == midIndex)
{
// evaluate current location with its right neighbor
if (input[midIndex] == input[midIndex + 1])
{
return midIndex + 1;
}
// fork right
leftIndex = midIndex;
}
else
{
// evaluate current location with its left neighbor
if (input[midIndex] == input[midIndex - 1])
{
return midIndex;
}
// fork left
rightIndex = midIndex;
}
midIndex = (int)(((uint)leftIndex + (uint)rightIndex) >> 1);
} while (leftIndex < midIndex && midIndex < rightIndex);
return -1;
}
n
, filled with numbers starting at 0 and incrementing by 1 (besides one duplicate) then the largest element isn-2
(as can be seen in your example). \$\endgroup\$dup
to0
. It will be set before it is returned (and it is never read before then). Heck, you could get rid ofdup
entirely and just return where you set it - if you aren't dead set on using the Single Return Path. \$\endgroup\$left
andright
supposed to be set to when the function is initially called? \$\endgroup\$n
is9
. Am I missing something? \$\endgroup\$