Поиск дубликатов в массиве
Условие
Напишите функцию, которая находит все дубликаты в массиве целых чисел.
Пример
Вход: [1, 2, 3, 2, 4, 3, 5] Выход: [2, 3]
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Основные подходы к поиску дубликатов в массиве целых чисел
Задача поиска дубликатов в массиве – классическая и часто встречается на собеседованиях. Существует несколько подходов, каждый с разной эффективностью и требованиями к памяти. Основное внимание уделяется выбору алгоритма в зависимости от ограничений: можно ли использовать дополнительные структуры данных, допустима ли сортировка исходного массива, важна ли сложность по времени или памяти.
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)
На собеседовании важно не только дать код, но и:
- Обсудить сложность алгоритма (время и память).
- Рассмотреть edge cases: пустой массив, массив без дубликатов, массив где все элементы одинаковы.
- Предложить альтернативы и обосновать выбор.
- Написать чистый код с комментариями.