Хеш-таблицы
Дубликат
Проверить содержит ли массив дубликаты
Постановка задачи
Описание (LeetCode)
Дан целочисленный массив nums, верните true если любое значение содержится в
массиве по крайней мере дважды, и верните false, если каждый элемент уникален.
Пример 1:
nums = [1, 2, 3, 1]
Результат:
true
Пояснение:
Элемент 1 содержится в массиве дважды в индексах 0 и 3.
Пример 2:
nums = [1, 2, 3, 4]
Результат:
false
Пояснение:
Все элементы уникальны в массиве.
Пример 3:
nums = [1, 1, 1, 3, 3, 4, 3, 2, 4, 2]
Результат:
true
Ограничения:
1 <= nums.length <= 10⁵-10⁹ <= nums[i] <= 10⁹
Решение
Это простая задача для разминки, которая проверяет умение работать с хеш-таблицами.
"Решение в лоб" - это интуитивный способ, который первым приходит в голову. Его
логика проста: нужно проверить каждую пару чисел в массиве. Мы берем первое
число и сравниваем его со всеми остальными. Затем берем второе и сравниваем с
оставшимися, и так далее. Если на любом этапе мы находим два одинаковых числа -
возвращаем true. Понятно, что такое решение имеет временную сложность O(n²)
и не является проходным для интервью.
В реальной разработке, однако, это может использоваться (очень редко), когда вы на 100% уверены, что массив всегда будет крошечным (например, не больше 5-10 элементов), и вы не хотите выделять лишнюю память под хеш-таблицу.
Более эффективный способ решения - использовать хеш-таблицу для поддержки множества. Множество хранит только уникальные значения, а проверка наличия элемента очень быстрая.
Основные шаги:
- Создаем пустое множество (хеш-таблицу).
- Проходим по массиву:
- Если число уже есть в множестве, мы нашли дубликат и возвращаем
true. - Если нет, добавляем число в множество.
- Если число уже есть в множестве, мы нашли дубликат и возвращаем
- Если цикл закончился - дубликатов нет, возвращаем
false.
Оценка сложности
Временная сложность
Мы проходим по массиву ровно один раз с помощью одного цикла и на каждом шаге
читаем из хеш-таблицы или добавляем в нее элемент. Операция чтения или
добавления элемента в хеш-таблицу в среднем занимает O(1), поэтому итоговая
временная сложность получается O(n).
Пространственная сложность
Так как мы используем хеш-таблицу, в худшем случае мы сохраним все элементы в таблице.
Таким образом пространственная сложность решения будет равна: O(n).
В этой задаче наша цель - это показать, как можно использовать хеш-таблицы для поиска дубликатов.
Есть другое решение, если нельзя использовать дополнительную память, - через
сортировку массива. Сортируем массив и потом проходим циклом и сравниваем каждый
элемент со следующим. Если они равны, мы нашли дубликат. Такое решение обычно
будет O(n * log n) по времени (сортировка), но O(1) по памяти.
Код решения
Приведем код решения.
func containsDuplicate(nums []int) bool {
seen := make(map[int]struct{})
for _, num := range nums {
if _, exists := seen[num]; exists {
return true
}
seen[num] = struct{}{}
}
return false
}
Итоги
Выбор решения зависит от того, что важнее в конкретной ситуации: скорость или память.
-
Используйте хеш-таблицу, если важна скорость. Это "золотой стандарт" для этой задачи: работает максимально быстро
O(n), жертвуя объемом оперативной памяти. -
Используйте сортировку, если важна память. Если вы работаете в условиях жестко ограниченной памяти, например во встроенных системах или при обработке гигантских массивов, сортировка позволяет избежать создания большой вспомогательной структуры данных.
-
Главный урок задачи: умение переходить от полного перебора к использованию хеш-таблиц - это базовый навык, который позволяет ускорять алгоритмы с квадратичной до линейной сложности.