The question is really just βhave I seen this number before?β repeated for every element. Answering that question fast β in O(1) instead of by rescanning β is what separates the brute force from the optimal solution.
Brute Force
Time O(nΒ²)Space O(1)Compare every pair of elements. If any two match, return true.
class Solution: def hasDuplicate(self, nums: list[int]) -> bool: n = len(nums) for i in range(n): for j in range(i + 1, n): if nums[i] == nums[j]: return True return FalseCorrect, since it checks every possible pair, but for each of the n starting indices it may rescan almost the entire rest of the array β O(nΒ²) comparisons in the worst case (no duplicates at all).
Sort First
Time O(n log n)Space O(1)If we sort the array, duplicates end up next to each other. A single linear pass then checks adjacent pairs.
class Solution: def hasDuplicate(self, nums: list[int]) -> bool: nums.sort() for i in range(1, len(nums)): if nums[i] == nums[i - 1]: return True return FalseSorting dominates the cost at O(n log n), and an in-place sort needs no extra array β O(1) auxiliary space (ignoring the sortβs own recursion stack). Better than brute force, but we can still do better by trading a little space for linear time.
Hash Set
OptimalTime O(n)Space O(n)Walk the array once, keeping a set of numbers seen so far. Before adding the current number, check whether it is already in the set β if it is, we have found our duplicate immediately.
class Solution: def hasDuplicate(self, nums: list[int]) -> bool: seen = set() for num in nums: if num in seen: return True seen.add(num) return FalseTracing nums = [1, 2, 3, 1]:
Hash Map
num=1 is not in the set yet. Add it.
Correctness: the set always holds exactly the elements seen before the current index, so a hit means some earlier index shares this value β a true duplicate. If the loop finishes without a hit, every element was distinct.
Complexity: each of the n elements does one average-case O(1) set lookup and insert β O(n) time. The set can grow to hold all n elements β O(n) space. This trades the sortβs O(n log n) for linear time by spending memory instead.