Массив Домашнее задание Вопрос - PullRequest
40 голосов
/ 20 июля 2009

Вам дан массив с целыми числами от 1 до 1 000 000. Одно целое число в массиве дважды. Как вы можете определить, какой? Можете ли вы придумать способ сделать это, используя немного дополнительной памяти.

Алго:

  • Решение 1:
    1. Иметь хеш-таблицу
    2. Итерация по массиву и сохранение его элементов в хеш-таблице
    3. Как только вы найдете элемент, который уже находится в хеш-таблице, это элемент dup
    • Работает за O (n) время и только за 1 проход
    Минусы:
    • Используется O (n) дополнительной памяти
Solution2:
  1. Сортировка массива с использованием сортировки слиянием (O (nlogn) по времени)
  2. Снова разберитесь, и если вы дважды увидите элемент, вы получите дубликат.
  • не используется дополнительная память
Минусы:
  • Время работы больше, чем O (n)

Ребята, вы можете придумать какое-нибудь лучшее решение?

Ответы [ 9 ]

33 голосов
/ 20 июля 2009

Вопрос немного двусмысленный; когда запрос «какой» означает ли это возвращение дублируемого значения или позиции в последовательности дублированного? Если первый, любое из следующих трех решений будет работать; если это последнее, то первое, что поможет.

Решение № 1: предполагается, что массив неизменен

Создание растрового изображения; установите n -й бит при выполнении итерации по массиву. Если бит уже установлен, вы нашли дубликат. Он работает по линейному времени и будет работать с массивом любого размера.

Растровое изображение должно быть создано с таким количеством битов, сколько есть возможных значений в массиве. Выполняя итерацию по массиву, вы проверяете n -й бит в массиве. Если он установлен, вы нашли свой дубликат. Если это не так, то установите его. (Логика для этого может быть замечена в псевдокоде в этой записи в Википедии для битовых массивов или для использования System.Collections.BitArray .)

Решение # 2: предполагается, что массив является изменяемым

Сортируйте массив, а затем выполните линейный поиск, пока текущее значение не станет равным предыдущему. Использует меньше всего памяти. Бонусные баллы за изменение алгоритма сортировки для обнаружения дубликата во время операции сравнения и преждевременного завершения.

Решение № 3: (предполагается, что длина массива = 1 000 001)

  1. Суммируйте все целые числа в массиве.
  2. Из этого вычтите сумму целых чисел от 1 до 1 000 000 включительно.
  3. То, что осталось, будет вашим дублированным значением.

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

Недостатком является то, что вам нужно выполнить весь цикл, чтобы найти ответ.

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

9 голосов
/ 20 июля 2009

При условии, что все числа от 1 до 1 000 000 находятся в массиве , сумма всех чисел от 1 до 1 000 000 составляет (1,000,000)*(1,000,000 + 1)/2 = 500,000 * 1,000,001 = 500,000,500,000.

Так что просто сложите все числа в массиве, вычтите 500 000 500 000, и у вас останется число, которое произошло дважды.

O (n) время и O (1) память.

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

Затем мы можем использовать фильтр Блума для обнаружения потенциальных дубликатов за O (n) время и проверки каждого кандидата за O (n) время.

6 голосов
/ 20 июля 2009

Этот код Python является модификацией QuickSort :

def findDuplicate(arr):
    orig_len = len(arr)
    if orig_len <= 1:
        return None
    pivot = arr.pop(0)
    greater = [i for i in arr if i > pivot]
    lesser = [i for i in arr if i < pivot]
    if len(greater) + len(lesser) != orig_len - 1:
        return pivot
    else:
        return findDuplicate(lesser) or findDuplicate(greater)

Он находит дубликат в O (n logn)), я думаю. Он использует дополнительную память в стеке, но его можно переписать, чтобы использовать только одну копию исходных данных, я считаю:

def findDuplicate(arr):
    orig_len = len(arr)
    if orig_len <= 1:
        return None
    pivot = arr.pop(0)
    greater = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] > pivot]
    lesser = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] < pivot]
    if len(arr):
        return pivot
    else:
        return findDuplicate(lesser) or findDuplicate(greater)

Понимания списка, которые производят больше и меньше уничтожают оригинал с помощью вызовов pop (). Если arr не является пустым после удаления больше и меньше , то должен быть дубликат, и он должен быть pivot .

Код страдает от обычных проблем переполнения стека отсортированных данных, поэтому требуется либо случайный свод, либо итеративное решение, которое ставит в очередь данные:

def findDuplicate(full):
    import copy
    q = [full]
    while len(q):
        arr = copy.copy(q.pop(0))
        orig_len = len(arr)
        if orig_len > 1:
            pivot = arr.pop(0)
            greater = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] > pivot]
            lesser = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] < pivot]
            if len(arr):
                return pivot
            else:
                q.append(greater)
                q.append(lesser)
    return None

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

Так много для информатики. Наивный алгоритм забивает мой код на python, возможно, из-за алгоритма сортировки python:

def findDuplicate(arr):
    arr = sorted(arr)
    prev = arr.pop(0)
    for element in arr:
        if element == prev:
            return prev
        else:
            prev = element
    return None
2 голосов
/ 20 июля 2009

Подсказка: используйте свойство, что A XOR A == 0 и 0 XOR A == A.

2 голосов
/ 20 июля 2009

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

например. Реализация сортировки слиянием на месте.

http://en.wikipedia.org/wiki/Merge_sort

0 голосов
/ 26 июля 2009

Сортировка целых чисел, сортировка их по месту, где они должны быть. Если вы получили «столкновение», то вы нашли правильный номер.

пробел сложности O (1) (то же пространство, которое может быть перезаписано) временная сложность меньше, чем O (n), потому что вы получите статистически найденное столкновение, прежде чем доберетесь до конца.

0 голосов
/ 25 июля 2009
def singleton(array):
  return reduce(lambda x,y:x^y, array)
0 голосов
/ 20 июля 2009

А как насчет проблемы поиска ВСЕХ дубликатов? Может ли это быть сделано менее чем за O (n ln n) время? (Сортировка и сканирование) (Если вы хотите восстановить исходный массив, сохраните исходный индекс и измените порядок после конца, что можно сделать за O (n) раз)

0 голосов
/ 20 июля 2009

В качестве варианта вашего решения (2) вы можете использовать radix sort . Нет дополнительной памяти, и будет работать в линейное время Вы можете утверждать, что время также зависит от размера представления чисел, но вы уже дали для этого границы: радикальная сортировка выполняется за время O (k n), где k - количество цифр, которые вы можете отсортировать за каждый проход. Это делает весь алгоритм O (7n) для сортировки плюс O (n) для проверки дублированного числа, а именно O (8n) = O (n).

Плюсы:

  • Нет дополнительной памяти
  • О (п)

Минусы:

  • Нужно восемь O (n) проходов.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...