← Назад к вопросам

Поиск дубликатов в массиве

2.0 Middle🔥 181 комментариев
#Теория тестирования

Условие

Напишите функцию, которая находит все дубликаты в массиве целых чисел.

Пример

Вход: [1, 2, 3, 2, 4, 3, 5] Выход: [2, 3]

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Основные подходы к поиску дубликатов в массиве целых чисел

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

1. Использование HashSet (или словаря) – наиболее эффективный по времени подход

Этот метод использует дополнительную память для отслеживания уже встреченных элементов. Он работает за линейное время O(n) и требует O(n) дополнительной памяти (в худшем случае).

Алгоритм:

  • Проходим по массиву.
  • Для каждого элемента проверяем, присутствует он уже в HashSet (seen).
  • Если присутствует – добавляем в результат (duplicates).
  • Если нет – добавляем в seen.
def find_duplicates_hash(nums):
    seen = set()
    duplicates = set()  # Используем set для уникальности дубликатов в результате
    
    for num in nums:
        if num in seen:
            duplicates.add(num)
        else:
            seen.add(num)
    
    return list(duplicates)  # Возвращаем список

# Пример использования:
nums = [1, 2, 3, 2, 4, 3, 5]
print(find_duplicates_hash(nums))  # Вывод: [2, 3]

Преимущества: Максимальная скорость – один проход по массиву. Недостатки: Использует дополнительную память.

2. Сортировка массива – подход с балансом времени и памяти

Если модификация исходного массива допустима или использование дополнительной памяти сильно ограничено, можно отсортировать массив и найти дубликаты в одном проходе по уже отсортированным данным. Сложность становится O(n log n) по времени (из-за сортировки), но память O(1) (если не считать память для сортировки, которая может быть O(1) для некоторых алгоритмов).

Алгоритм:

  • Сортируем массив (например, sorted() в Python).
  • Проходим по массиву, сравнивая текущий элемент с предыдущим.
  • Если они равны – это дубликат.
def find_duplicates_sort(nums):
    nums_sorted = sorted(nums)  # Создаем отсортированную копию, O(n) памяти
    duplicates = []
    
    for i in range(1, len(nums_sorted)):
        if nums_sorted[i] == nums_sorted[i-1]:
            duplicates.append(nums_sorted[i])
    
    # Если нужно вернуть уникальные дубликаты, преобразуем в set и обратно
    return list(set(duplicates))

# Или вариант с сортировкой исходного массива (если допустимо):
def find_duplicates_sort_inplace(nums):
    nums.sort()  # Модифицируем исходный массив, O(1) доп. памяти
    duplicates = []
    for i in range(1, len(nums)):
        if nums[i] == nums[i-1]:
            duplicates.append(nums[i])
    return list(set(duplicates))

nums = [1, 2, 3, 2, 4, 3, 5]
print(find_duplicates_sort(nums))  # Вывод: [2, 3]

Преимущества: Минимальная дополнительная память (если сортировка in-place). Недостатки: Медленнее хэш-таблицы из-за сортировки.

3. Использование счетчика (Counter) – удобный вариант для анализа частот

В Python, если задача не только в поиске дубликатов, но и в анализе частот, удобно использовать collections.Counter. Он также работает за O(n) и использует O(n) памяти.

Алгоритм:

  • Создаем Counter из массива.
  • Фильтруем элементы с количеством > 1.
from collections import Counter

def find_duplicates_counter(nums):
    count = Counter(nums)
    duplicates = [num for num, freq in count.items() if freq > 1]
    return duplicates

nums = [1, 2, 3, 2, 4, 3, 5]
print(find_duplicates_counter(nums))  # Вывод: [2, 3]

Преимущества: Краткость и удобство, сразу получаем частоту каждого элемента. Недостатки: Требует дополнительной памяти для хранения счетчиков всех элементов.

Рекомендации по выбору метода на собеседовании

  • Стандартный ответ: Использовать HashSet. Это демонстрирует знание эффективных структур данных. Обязательно упомянуть сложность O(n) по времени и памяти.
  • Если спрашивают об оптимизации памяти, предложить метод сортировки. Важно отметить, что сложность по времени становится O(n log n).
  • В Python для простоты можно предложить Counter, но стоит объяснить его внутреннюю работу (он также использует хэш-таблицу).
  • Важный нюанс: В условии часто требуется вернуть дубликаты, а не все повторения. Например, если элемент встречается 3 раза, в результат он должен попасть только один раз (как в примере: [2, 3]). Для этого в реализации с HashSet результат также собирается в set, или в конце список преобразуется через set().

Пример полного ответа с оптимизацией и учетом всех случаев

def find_all_duplicates(nums):
    """
    Находит все дубликаты в массиве целых чисел.
    Возвращает список дубликатов без повторений.
    """
    if not nums:
        return []
    
    seen = set()
    duplicates = set()
    
    for num in nums:
        if num in seen:
            duplicates.add(num)
        else:
            seen.add(num)
    
    return list(duplicates)

На собеседовании важно не только дать код, но и:

  1. Обсудить сложность алгоритма (время и память).
  2. Рассмотреть edge cases: пустой массив, массив без дубликатов, массив где все элементы одинаковы.
  3. Предложить альтернативы и обосновать выбор.
  4. Написать чистый код с комментариями.