2015-04-13 2 views
0

У меня есть массив размером «n» с unsorted и reapeated integers между [0-99]. Я уже знаю, что одного номера нет. Итак, каков самый быстрый способ найти недостающее число? Это мое решение до сих пор в С.Поиск недостающего числа в несортированном массиве с повторяющимися значениями

int find_missing_number(int array[], int n) 
{ 
    char checker[100] = {0}; 
    int xor = 0, counter = 0, i, temp; 
    //xor = (n%4==0) ? 0 : (n%4==1) ? n-1 : (n%4==2) ? 1 : n; 

    for(i = 0; i < n; i++) 
     if(checker[temp = array[i]] == 0) 
     { 
      checker[temp] = 1; 
      xor ^= temp; 
      if(++counter == 99) 
       return xor; 
     } 

    return -1; 
} 
+0

Вы должны объяснить логику. Из структуры это выглядит как линейное время, которое лучше всего, о чем я могу думать. –

+0

@luis Если значения могут быть повторены, то почему только один номерr пропущен? –

+0

Это вопрос интервью? Возможно более одного недостатка? Является ли значение n всегда 99 (оно должно быть, если отсутствует только одно число, и массив содержит все остальные элементы из 0-99 один раз)? – user2407394

ответ

1

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

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

int find_missing_number(int array[], int n)  
{ 
    int checker[100] = {0}; 

    for(int i = 0; i < n; i++) 
     checker[array[i]]++; 

    for(int i=0; i <100; i++) 
     if(0 == checker[i]) 
      return i; 
} 

Это уменьшит операции, которые зависят от n, делая код быстрее для большего n.

+0

Нет, размер массива * не * гарантированно равен 99. По комментариям OP могут повторяться значения.Это чрезвычайно умно для случая, когда повторений нет, но он ломается, если более одного значения появляется четное количество раз (включая нулевые времена). –

+0

Спасибо, я пропустил это в первый раз. – user2407394

+0

Это выглядит хорошо для меня, за исключением того, что он должен «возвращать i». –

1

Вы можете сделать это в O (n). Перейдем через массив и вычислим сумму всех чисел. Теперь сумма натуральных чисел от 1 до N может быть выражена как Nx (N + 1)/2. В вашем случае N = 99. Вычтите сумму массива из Nx (N + 1)/2, где N = 99. Это недостающее число. Пустой слот может быть обнаружен во время итерации, в которой вычисляется сумма.

Я не совсем уверен, если это более быстрый метод или нет, но вариант для вас попробовать.

Редактировать: Это будет работать, если у вас не было повторяющихся значений.

+0

Очень умный, но он ломается, когда повторяются числа, так как OP должен быть размещен. –

+0

Ах, да, я перечитал это и понял, что он повторил цифры. Очень интересная проблема. Спасибо за уточнение. – Javia1492

+0

@EugeneSh. Я не думаю, что существует неоднозначность повторяющихся чисел. Требуется значение из диапазона 0-99, которое появляется нулевое время во входном массиве. Повторные номера не путают спецификацию (в отличие от возможности наличия более чем одного значения в диапазоне, который появляется в нулевом времени). –

1

Учитывая, что у вас могут быть дубликаты, я не вижу никакого способа сохранить какую-то запись, какие значения были замечены, которую вы можете прочитать на основе стоимости. Подходы, основанные на одном и том же разновидности односторонней хэширующей функции (два из которых были предложены, а затем убраны), не могут выполнять свою работу самостоятельно. Может быть, однако, что вы можете сэкономить усилия сканирования вашей записи замеченных значений после заполнения ее, объединив ее с хэш-подобной функцией. Например,

int find_missing_number(int array[], int n) 
{ 
    char checker[100] = {0}; 
    int xor = XOR_OF_1_TO_100; 
    int i; 

    for(i = 0; i < n; i++) { 
     xor ^= (checker[array[i]] ? 0 : array[i] + 1); 
     checker[array[i]] = 1; 
    } 

    return xor - 1; 
} 

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

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

Кроме того, элементы checker массива имеют тип char, что позволяет в четыре раза больше, чтобы находиться в строке кэша процессора сразу, чем если бы они были типа int (предполагая, что 1-байтовое char с и 4-байтовый int ы) ,

Обратите внимание, что я избегаю подсчета различных значений или иначе ветвящихся внутри цикла (тройное выражение может быть реализовано без ветки). Избегание счетного и условного оператора ускорит случай, когда действительно отсутствует одно значение, и может или не может на практике замедлить случай, когда ни один отсутствует. Коэффициент усиления может возникнуть из-за того, что он не имеет меньше кода - иногда такие упрощения могут позволить компилятору генерировать более эффективные последовательности команд.

Конечно, все дело в нежелательной (и проблема не указана), если может отсутствовать более одного значения.

+0

Вы правы с использованием «char» вместо «int», это более низкое потребление памяти, и вы тоже правы с переменными регистра, а компилятор отвечает за эту задачу. Что касается предложения об удалении переменной «счетчик», я должен ее протестировать, потому что «n» он действительно большой, и он не всегда работает всегда с 1..n. но вы снова правы: «Избегайте подсчета и условного утверждения». – luis

+0

В случае «худшего» случая переменная 'counter' не помогает вообще. Помогает ли это в среднем случае, зависит от множества факторов, но среди них значительная величина «n», распределение значений во входном массиве и распределение вероятности недостающего значения. Обратите также внимание на то, что если есть вероятность, что во входном массиве не будет отображаться какое-либо значение, тогда вам нужно сканировать весь массив во всех остальных случаях, чтобы убедиться, что на самом деле имеется непредставимое значение. –

1

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

 if(++counter == 100) 
      return -1; 

к этому:

 if(++counter == 99) 
      return xor; 

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

+0

Вы правы, я уже редактировал вопросы. Спасибо – luis

1

У Javia1492 была основная идея в его ответе: суммирование чисел.

Аргумент checker не позволяет суммировать вдвое большее число.

Переменная counter подсчитывает числа различных чисел, чтобы завершить поиск, прежде чем тестировать целые числа n в массиве, если у вас уже есть 99 различных значений.

Он также работает с оператором xor, как и вы, но мне это не очень нравится, потому что 1^2^3^..^99 == 0 - это своего рода частный случай, с которым вы не можете обобщить 1^2^..^N.

int find_missing_number(int array[], int n) 
{ 
    char checker[100]; 
    register int sum = 0, counter = 0, i, temp; 

    memset(checker, 0, sizeof(checker)); 
    for (i = 0; i < n; i++) 
    { 
     if (checker[temp = array[i]] == 0) 
     { 
      checker[temp] = 1; 
      sum += temp; 
      if (++counter == 99) 
       break; 
     } 
    } 
    return 4950 - sum; // (100*99)/2 - sum 
} 
+0

Прежде чем «Задайте этот вопрос», я искал похожие вопросы, и я нахожу это решение, которое вы предлагаете, что кажется очень интересным, но я также нашел решение xor. Я реализовал и протестировал оба, и решение xor работает быстрее, благодаря простоте вычислений xor по сравнению с суммами и вычитанием. – luis

 Смежные вопросы

  • Нет связанных вопросов^_^