217. Contains Duplicate
Problem Description
You are given an integer array nums
. Your task is to determine if there are any duplicate values in the array.
Return true
if any value appears at least twice in the array. Return false
if every element in the array is distinct (appears only once).
For example:
- If
nums = [1, 2, 3, 1]
, you should returntrue
because the value1
appears twice - If
nums = [1, 2, 3, 4]
, you should returnfalse
because all elements are unique
The solution approach sorts the array first, then checks if any two adjacent elements are equal. After sorting, duplicate elements will be placed next to each other, making them easy to detect by comparing consecutive pairs. The pairwise
function creates pairs of adjacent elements (a, b)
from the sorted array, and any()
returns true
if at least one pair has equal elements.
Intuition
When we need to find duplicates in an array, we need a way to compare elements efficiently. One key observation is that if we arrange the array in sorted order, any duplicate elements will end up right next to each other.
Think about it: if we have the array [3, 1, 4, 1, 5]
and sort it to get [1, 1, 3, 4, 5]
, the duplicate 1
s are now adjacent. This property holds for any duplicates in the array - sorting brings identical values together.
Once sorted, we only need to check consecutive pairs of elements. If any two adjacent elements are equal, we know there's a duplicate. This reduces our problem from comparing every element with every other element (which would be inefficient) to just comparing neighbors.
The pairwise
function generates these consecutive pairs (nums[0], nums[1])
, (nums[1], nums[2])
, and so on. We can then use any()
to check if at least one pair has matching elements. As soon as we find one matching pair, we can return true
. If we check all pairs and find no matches, all elements must be distinct.
This approach leverages the sorting operation to organize our data in a way that makes duplicate detection straightforward - transforming a potentially complex comparison problem into a simple linear scan of adjacent elements.
Solution Approach
The implementation follows the sorting-based approach to detect duplicates:
-
Sort the array: We first sort the input array
nums
using Python's built-insorted()
function. This operation arranges all elements in ascending order, with the time complexity ofO(n log n)
. -
Generate consecutive pairs: The
pairwise()
function (available in Python's itertools) creates an iterator of consecutive element pairs from the sorted array. For example, if the sorted array is[1, 2, 2, 3]
, pairwise generates:(1, 2)
,(2, 2)
,(2, 3)
. -
Check for equal pairs: We use a generator expression
a == b for a, b in pairwise(sorted(nums))
to check if any pair has equal elements. This comparison is done lazily - it doesn't check all pairs upfront but evaluates them one by one. -
Early termination with
any()
: Theany()
function returnstrue
as soon as it finds the first pair wherea == b
. This provides early termination - if duplicates are found early in the sorted array, we don't need to check the remaining pairs.
The entire solution is elegantly condensed into a single line:
return any(a == b for a, b in pairwise(sorted(nums)))
Time Complexity: O(n log n)
due to sorting, where n
is the length of the array.
Space Complexity: O(n)
for creating the sorted array (or O(1)
if we consider the sorting space as auxiliary).
This approach is efficient and clean, leveraging Python's built-in functions to create a concise solution that's easy to understand and maintain.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the array nums = [4, 2, 7, 2, 1]
.
Step 1: Sort the array
- Original array:
[4, 2, 7, 2, 1]
- After sorting:
[1, 2, 2, 4, 7]
- Notice how the duplicate
2
s are now adjacent to each other
Step 2: Generate consecutive pairs using pairwise
- From the sorted array
[1, 2, 2, 4, 7]
, we create pairs:- Pair 1:
(1, 2)
- comparing indices 0 and 1 - Pair 2:
(2, 2)
- comparing indices 1 and 2 - Pair 3:
(2, 4)
- comparing indices 2 and 3 - Pair 4:
(4, 7)
- comparing indices 3 and 4
- Pair 1:
Step 3: Check each pair for equality
- Pair
(1, 2)
:1 == 2
? No - Pair
(2, 2)
:2 == 2
? Yes! Found a duplicate
Step 4: Return result
- Since we found a matching pair
(2, 2)
, theany()
function immediately returnstrue
- We don't need to check the remaining pairs
(2, 4)
and(4, 7)
due to early termination
The function returns true
, correctly identifying that the array contains duplicates.
Contrast with a non-duplicate example: If we had nums = [4, 2, 7, 1]
:
- After sorting:
[1, 2, 4, 7]
- Pairs:
(1, 2)
,(2, 4)
,(4, 7)
- None of these pairs have equal elements, so
any()
returnsfalse
Solution Implementation
1from typing import List
2from itertools import pairwise
3
4class Solution:
5 def containsDuplicate(self, nums: List[int]) -> bool:
6 """
7 Check if the array contains any duplicate elements.
8
9 Args:
10 nums: List of integers to check for duplicates
11
12 Returns:
13 True if any value appears at least twice, False otherwise
14 """
15 # Sort the array so duplicate elements will be adjacent
16 sorted_nums = sorted(nums)
17
18 # Check each pair of consecutive elements for equality
19 # pairwise() creates sliding window pairs: (a[0], a[1]), (a[1], a[2]), ...
20 # any() returns True if at least one pair has equal elements
21 return any(a == b for a, b in pairwise(sorted_nums))
22
1class Solution {
2 /**
3 * Determines if an array contains any duplicate elements.
4 *
5 * @param nums The input array of integers
6 * @return true if any value appears at least twice, false otherwise
7 */
8 public boolean containsDuplicate(int[] nums) {
9 // Sort the array to place duplicate elements adjacent to each other
10 Arrays.sort(nums);
11
12 // Iterate through the sorted array and check adjacent elements
13 for (int i = 0; i < nums.length - 1; i++) {
14 // If two adjacent elements are equal, we found a duplicate
15 if (nums[i] == nums[i + 1]) {
16 return true;
17 }
18 }
19
20 // No duplicates found after checking all adjacent pairs
21 return false;
22 }
23}
24
1class Solution {
2public:
3 bool containsDuplicate(vector<int>& nums) {
4 // Sort the array in ascending order
5 sort(nums.begin(), nums.end());
6
7 // Iterate through the sorted array and check adjacent elements
8 for (int i = 0; i < nums.size() - 1; ++i) {
9 // If two adjacent elements are equal, we found a duplicate
10 if (nums[i] == nums[i + 1]) {
11 return true;
12 }
13 }
14
15 // No duplicates found
16 return false;
17 }
18};
19
1/**
2 * Checks if an array contains any duplicate elements
3 * @param nums - Array of numbers to check for duplicates
4 * @returns true if duplicates exist, false otherwise
5 */
6function containsDuplicate(nums: number[]): boolean {
7 // Sort the array in ascending order
8 nums.sort((a: number, b: number) => a - b);
9
10 // Store the array length for iteration
11 const arrayLength: number = nums.length;
12
13 // Iterate through the sorted array starting from index 1
14 for (let i: number = 1; i < arrayLength; i++) {
15 // Compare current element with previous element
16 // If they are equal, we found a duplicate
17 if (nums[i - 1] === nums[i]) {
18 return true;
19 }
20 }
21
22 // No duplicates found after checking all adjacent pairs
23 return false;
24}
25
Time and Space Complexity
Time Complexity: O(n log n)
, where n
is the length of the array nums
.
The time complexity is dominated by the sorted()
function, which uses Timsort algorithm with O(n log n)
time complexity in the average and worst cases. After sorting, the pairwise()
function iterates through the sorted array once, creating n-1
pairs, and the any()
function checks these pairs for equality, both taking O(n)
time. Since O(n log n) + O(n) = O(n log n)
, the overall time complexity is O(n log n)
.
Space Complexity: O(n)
, where n
is the length of the array nums
.
The sorted()
function creates a new sorted list containing all elements from the input array, requiring O(n)
additional space. The pairwise()
function generates pairs on-the-fly as an iterator without storing all pairs simultaneously, so it only uses O(1)
extra space. Therefore, the overall space complexity is O(n)
.
Common Pitfalls
1. Python Version Compatibility Issue with pairwise()
The pairwise()
function was introduced in Python 3.10. If you're using an earlier version of Python, the code will raise an ImportError
or AttributeError
. This is a frequent issue in coding interviews or online judges that may use older Python versions.
Solution: Implement your own pairwise logic or use alternative approaches:
# Option 1: Manual pairwise implementation
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
sorted_nums = sorted(nums)
for i in range(len(sorted_nums) - 1):
if sorted_nums[i] == sorted_nums[i + 1]:
return True
return False
# Option 2: Use zip with offset
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
sorted_nums = sorted(nums)
return any(a == b for a, b in zip(sorted_nums, sorted_nums[1:]))
2. Inefficiency for Large Arrays with Early Duplicates
While sorting provides a clean solution, it always takes O(n log n) time even if duplicates exist at the beginning of the array. For instance, if nums = [1, 1, 2, 3, ..., 1000000]
, we still sort the entire array despite finding duplicates immediately.
Solution: Use a HashSet approach for better average-case performance:
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
seen = set()
for num in nums:
if num in seen:
return True
seen.add(num)
return False
# Or even simpler:
# return len(nums) != len(set(nums))
3. Memory Overhead from Creating Sorted Copy
The sorted()
function creates a new list, consuming O(n) additional space. In memory-constrained environments, this could be problematic for very large arrays.
Solution: If modifying the original array is acceptable, use in-place sorting:
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
nums.sort() # In-place sorting
for i in range(len(nums) - 1):
if nums[i] == nums[i + 1]:
return True
return False
4. Edge Case: Empty or Single-Element Arrays
While the current solution handles these correctly, developers might overlook testing these cases, especially when implementing manual iterations.
Solution: Always validate with edge cases:
- Empty array
[]
should returnFalse
- Single element
[1]
should returnFalse
- Two identical elements
[1, 1]
should returnTrue
What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!