Таким образом, существует алгоритм, который принимает O (n ^ 2), который не требует модификации входного массива и занимает постоянное пространство.
Сначала предположим, что вы знаете n
и m
. Это линейная операция, поэтому она не добавляет дополнительной сложности. Далее предположим, что существует один элемент, равный n
, и один элемент, равный n+m-1
, а все остальные находятся в [n, n+m)
. Учитывая это, мы можем свести проблему к наличию массива с элементами в [0, m)
.
Теперь, поскольку мы знаем, что элементы ограничены размером массива, мы можем рассматривать каждый элемент как узел с одной ссылкой на другой элемент; другими словами, массив описывает ориентированный граф. В этом ориентированном графе, если нет повторяющихся элементов, каждый узел принадлежит циклу, то есть узел доступен из себя за m
или менее шагов. Если есть дублирующий элемент, то существует один узел, который вообще недоступен сам по себе.
Итак, чтобы обнаружить это, вы проходите весь массив от начала до конца и определяете, возвращается ли каждый элемент к себе в <=m
шагах. Если какой-либо элемент недоступен в <=m
шагах, то у вас есть дубликат и вы можете вернуть false. В противном случае, когда вы закончите посещение всех элементов, вы можете вернуть true:
for (int start_index= 0; start_index<m; ++start_index)
{
int steps= 1;
int current_element_index= arr[start_index];
while (steps<m+1 && current_element_index!=start_index)
{
current_element_index= arr[current_element_index];
++steps;
}
if (steps>m)
{
return false;
}
}
return true;
Вы можете оптимизировать это, сохраняя дополнительную информацию:
- Запишите сумму длины цикла от каждого элемента, если цикл не посещает элемент перед этим элементом, назовите его
sum_of_steps
.
- Для каждого элемента только шаг
m-sum_of_steps
узлов. Если вы не возвращаетесь к начальному элементу и не посещаете элемент перед начальным элементом, вы нашли цикл, содержащий дубликаты элементов, и можете вернуть false
.
Это все еще O (n ^ 2), например {1, 2, 3, 0, 5, 6, 7, 4}
, но это немного быстрее.