Я новичок в изучении структур данных и алгоритмов и начинаю решать задачи NeetCode 150. Первая проблема, с которой я столкнулся, была несложной, я понимаю, почему она работает, но мне любопытно, почему лучшее решение предполагает использование HashSet вместо массива.
Я уверен, что речь идет о временной сложности, а это тема, которую мне трудно понять, поэтому я надеюсь, что кто-нибудь сможет дать мне простое объяснение, почему одно решение лучше другого, и будет ли оно действительно таковым. плохо использовать здесь массив вместо HashSet (если бы я использовал его в интервью, им бы это не понравилось).
Это решение, которое я придумал. Он практически идентичен, за исключением использования массивов и .append() вместо HashSet и .add(). Решение было технически правильным, но, судя по проведенным мной исследованиям, решение HashSet оказалось лучше/быстрее, и мне нужна помощь, чтобы понять, почему.
class Solution:
def hasDuplicate(self, nums: List[int]) -> bool:
newArray = []
for i in nums:
if i in newArray:
return True
newArray.append(i)
return False
Вот самое лучшее решение.
class Solution:
def hasDuplicate(self, nums: List[int]) -> bool:
hashset = set()
for n in nums:
if n in hashset:
return True
hashset.add(n)
return False
В чем различия? Также какое влияние окажет добавление nums.sort() на первый подход? Изменит ли это каким-либо образом время выполнения? Извините, если эти вопросы до боли очевидны, мне просто нужна помощь, чтобы разобраться в этих концепциях.
Если быть точным: У вас нет массива . У вас есть список.
Давайте рассмотрим использование оператора in
в операторе if
.
Для массива операция in
является операцией O(n) — элементы массива просматриваются последовательно, а в худшем случае (т. е. если искомый элемент отсутствует в массиве) он перебирает весь массив. Для хэш-набора операция in
является операцией O(1) — рассматриваемый элемент хешируется и проверяется по правому сегменту хэша (предостережение: это предполагает, что алгоритм хеширования является правильным, что в случае целых чисел должно быть).
Что касается сортировки, давайте начнем с самого простого: второй подход, использующий хеш-набор, представляет собой алгоритм O(n) (n элементов, каждый из которых имеет операцию O(1) для проверки того, находятся ли они в хеш-наборе, и если нет, добавьте их). Сортировка num
будет операцией O(nlog(n)), поэтому здесь она точно не поможет.
При первом подходе, где используется массив, он в конечном итоге тоже не поможет. Во-первых, давайте поймем временную сложность этого решения. nums
имеет n элементов, для каждого из которых ищется вспомогательный массив. Для первого элемента ищется массив нулевого размера, для второго — массив размера 1 и так далее. В конечном итоге это имеет сложность log(n), а поскольку это делается для n элементов, общая сложность равна O(nlog(n)). Таким образом, добавление сортировки определенно не улучшит решение на порядок.
Это мне очень помогло, спасибо. Я боролся с тем, что именно делает/назначает HashSet. Я исследовал это заранее и понял разницу, но не понял, что он хранит уникальные элементы и в правильном порядке (по крайней мере, для целых чисел я пока уверен в других типах данных).
«В конечном итоге это имеет сложность log(n)» - Хм? Как же так? Почему бы и нет?
@HishamIssa Наборы неупорядочены, не имеют «правильного порядка».
В Python наборы представляют собой хеш-таблицы, в которых быстрее проверить существование элемента. Его сложность O(1), потому что вы проверяете, взят индекс n или нет, в отличие от массивов, где вы проходите через каждый элемент, что приводит к сложности O(n).
Одним из основных преимуществ набора является то, что:
if n in hashset
не нужно перебирать каждый элемент, а здесь:if i in newArray
. По мере того, какnewArray
становится длиннее, это занимает больше времени, потому что нужно начинать с самого начала и просматривать каждый элемент, пока не будет найдено совпадение.