Я хотел бы уточнить решение @ AryanFirouzian и вернуть все дубликаты, используя yield return
. Кроме того, использование временной переменной упрощает код.
public static IEnumerable<int> FindDuplicates(int[] A)
{
for (int i = 0; i < A.Length; i++) {
int absAi = Math.Abs(A[i]);
if (A[absAi] < 0) {
yield return absAi;
} else {
A[absAi] *= -1;
}
}
}
Однако это решение не возвращает элемент с более низким индексом и, если существует более 2 одинаковых копий, оно будет возвращать одно и то же значение более одного раза. Другая проблема заключается в том, что 0 нельзя сделать отрицательным.
Лучшее решение исключает повторные результаты, но все равно возвращает второй индекс и имеет проблему с 0 значениями. Он также возвращает сам индекс, чтобы продемонстрировать проблему неправильного индекса
public static IEnumerable<(int index, int value)> FindDuplicates(int[] A)
{
for (int i = 0; i < A.Length; i++) {
int x = A[i] % A.Length;
if (A[x] / A.Length == 1) {
yield return (i, x);
}
A[x] += A.Length;
}
}
Проверено с
var A = new int[] { 3, 4, 2, 5, 2, 3, 3 };
foreach (var item in FindDuplicates(A)) {
Console.WriteLine($"[{item.index}] = {item.value}");
}
Возвращает
[4] = 2
[5] = 3
Мое окончательное решение, которое устраняет все эти проблемы (по крайней мере, я на это надеюсь): оно кодирует сам первый индекс, добавляя (i + 1) * A.Length
к первому вхождению значения. (i + 1)
потому что i
может быть 0
. Затем индекс может быть декодирован с помощью обратной операции (A[x] / A.Length) - 1
.
Тогда, поскольку мы хотим вернуть результат только по первому повторяющемуся значению, мы устанавливаем значение в отрицательное значение, чтобы исключить его из дальнейшей обработки. Впоследствии исходное значение можно получить с помощью Math.Abs(A[i]) % A.Length
.
public static IEnumerable<(int index, int value)> FindDuplicates(int[] A)
{
for (int i = 0; i < A.Length; i++) {
int x = Math.Abs(A[i]) % A.Length;
if (A[x] >= 0) {
if (A[x] < A.Length) { // First occurrence.
A[x] += (i + 1) * A.Length; // Encode the first index.
} else { // Second occurrence.
int firstIndex = (A[x] / A.Length) - 1; // Decode the first index.
yield return (firstIndex, x);
// Mark the value as handeled by making it negative;
A[x] *= -1; // A[x] is always >= A.Length, so no zero problem.
}
}
}
}
Возвращает ожидаемый результат
[2] = 2
[0] = 3
Наши элементы - это целые, которые не имеют идентичности. То есть мы можем вернуть один из дубликатов по любому индексу, поскольку два одинаковых целых не могут быть различены. В случае, если элементы имеют идентичность (они могут быть ссылочными типами с одинаковыми значениями, но разными ссылками или иметь дополнительные поля, не участвующие в тестировании на равенство), мы должны были бы вернуть первое вхождение с
yield return (firstIndex, Math.Abs(A[firstIndex]) % A.Length);
для удовлетворения всех требований.