найти повторяющееся число в массиве

Я отлаживаю ниже проблему и публикую решение, над которым я отлаживаю и работаю, решение или подобное опубликовано на нескольких форумах, но я думаю, что решение имеет ошибку, когда 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 ответов


  1. начните с двух указателей на первый элемент:быстро и медленно.
  2. определите 'move' как incrementing быстро на 2 шага (позиции) и медленно к 1.
  3. после каждого движение, проверьте, если медленно & быстро указывать на тот же узел.
  4. Если есть петля, в какой-то момент они будут. Это потому, что после того, как они оба находятся в цикле,быстро is движется вдвое быстрее, чем медленно и в конечном итоге "столкнется" с ним.
  5. скажем, они встречаются после K ходов. Это не обязательно повторяющийся элемент, так как это может быть не первый элемент цикла, достигаемый извне цикла.
    Назовите этот элемент X.
  6. обратите внимание, что быстро вышел 2k раз, и медленно вышел k раза.
  7. переместить быстро вернуться к нулю.
  8. неоднократно аванс быстро и медленно по одному шагу каждый, сравнивая после каждого шага.
  9. обратите внимание, что после очередного k шаги, медленно будут перемещены в общей сложности 2k шаги и быстро в общей сложности k шаги с самого начала, поэтому они снова будут указывать на X.
  10. обратите внимание, что если предыдущий шаг находится в цикле для обоих из них, оба указывали на X-1. Если предыдущий шаг был только на цикле для медленно, затем они указывали на различные элементы.
  11. то же самое для X-2, X-3, ...
  12. таким образом, в дальнейшем, первый раз, когда они указывают на один и тот же элемент, является первым элементом цикла, достигнутого извне цикла, который является повторяющимся элементом, который вы ищете.

ниже мой код, который использует алгоритм поиска циклов Флойда:

#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;
  }  

это преобразование требуется, так как если мы должны использовать подход хэширования,то должно быть столкновение для хэширования одного и того же ключа.

Я не могу придумать способ, которым можно использовать хэширование без дополнительного пространства и не изменять массив.