найти повторяющееся число в массиве
Я отлаживаю ниже проблему и публикую решение, над которым я отлаживаю и работаю, решение или подобное опубликовано на нескольких форумах, но я думаю, что решение имеет ошибку, когда num[0] = 0 или вообще num[x] = x? Я прав? Пожалуйста, не стесняйтесь поправлять меня, если я ошибаюсь.
учитывая массив чисел, содержащий n + 1 целых чисел, где каждое целое число находится между 1 и n (включительно), докажите, что должно существовать хотя бы одно дублирующее число. Предположим, что существует только один дубликат номер, найди дубликат.
Примечание.: Вы не должны изменять массив (предположим, что массив только для чтения). Вы должны использовать только константу, O (1) дополнительное пространство. Сложность выполнения должна быть меньше O (n2). В массиве есть только одно повторяющееся число, но его можно повторить несколько раз.
int findDuplicate3(vector<int>& nums)
{
if (nums.size() > 1)
{
int slow = nums[0];
int fast = nums[nums[0]];
while (slow != fast)
{
slow = nums[slow];
fast = nums[nums[fast]];
}
fast = 0;
while (fast != slow)
{
fast = nums[fast];
slow = nums[slow];
}
return slow;
}
return -1;
}
4 ответов
- начните с двух указателей на первый элемент:быстро и медленно.
- определите 'move' как incrementing быстро на 2 шага (позиции) и медленно к 1.
- после каждого движение, проверьте, если медленно & быстро указывать на тот же узел.
- Если есть петля, в какой-то момент они будут. Это потому, что после того, как они оба находятся в цикле,быстро is движется вдвое быстрее, чем медленно и в конечном итоге "столкнется" с ним.
- скажем, они встречаются после K ходов. Это не обязательно повторяющийся элемент, так как это может быть не первый элемент цикла, достигаемый извне цикла.
Назовите этот элемент X. - обратите внимание, что быстро вышел 2k раз, и медленно вышел k раза.
- переместить быстро вернуться к нулю.
- неоднократно аванс быстро и медленно по одному шагу каждый, сравнивая после каждого шага.
- обратите внимание, что после очередного k шаги, медленно будут перемещены в общей сложности 2k шаги и быстро в общей сложности k шаги с самого начала, поэтому они снова будут указывать на X.
- обратите внимание, что если предыдущий шаг находится в цикле для обоих из них, оба указывали на X-1. Если предыдущий шаг был только на цикле для медленно, затем они указывали на различные элементы.
- то же самое для X-2, X-3, ...
- таким образом, в дальнейшем, первый раз, когда они указывают на один и тот же элемент, является первым элементом цикла, достигнутого извне цикла, который является повторяющимся элементом, который вы ищете.
ниже мой код, который использует алгоритм поиска циклов Флойда:
#include <iostream>
#include <vector>
using namespace std;
int findDup(vector<int>&arr){
int len = arr.size();
if(len>1){
int slow = arr[0];
int fast = arr[arr[0]];
while(slow!=fast){
slow = arr[slow];
fast = arr[arr[fast]];
}
fast = 0;
while(slow!=fast){
slow = arr[slow];
fast = arr[fast];
}
return slow;
}
return -1;
}
int main() {
vector<int>v = {1,2,2,3,4};
cout<<findDup(v)<<endl;
return 0;
}
комментарий это работает, потому что нули не допускаются, поэтому первый элемент массива не является частью цикла, и поэтому первый элемент первого цикла, который мы находим, относится как к внешнему, так и к внутреннему циклу. Если бы нули были разрешены, это потерпело бы неудачу, если бы arr[0] был на цикле. Е. Г., [0,1,1].
сумма целых чисел от 1 до N = (N * (N + 1)) / 2
. Вы можете использовать это, чтобы найти дубликат -- sum целых чисел в массиве, а затем вычесть приведенную выше формулу из суммы. Это дубликат.
обновление: вышеуказанное решение основано на (возможно, недопустимом) предположении, что входной массив состоит из значений от 1 до N плюс один дубликат.
поскольку вы не можете использовать дополнительное пространство, использование другой хэш-таблицы будет исключено.
теперь, подходя к подходу хэширования на существующем массиве, его можно получить, если нам разрешено изменять массив на месте.
Algo:
1) Начните с первого элемента.
2) хэш первого элемента и применить преобразование к значению хэша.Предположим, это преобразование делает ценность -вэ.
3)Перейти к следующему элементу.Хэш элемента и перед применением преобразования проверьте, было ли уже применено преобразование.
4) Если да, то элемент является дубликатом.
код:
for(i = 0; i < size; i++)
{
if(arr[abs(arr[i])] > 0)
arr[abs(arr[i])] = -arr[abs(arr[i])];
else
cout<< abs(arr[i]) <<endl;
}
это преобразование требуется, так как если мы должны использовать подход хэширования,то должно быть столкновение для хэширования одного и того же ключа.
Я не могу придумать способ, которым можно использовать хэширование без дополнительного пространства и не изменять массив.