Problem overview
The method Intersection receives two int[] arrays, first and second. It returns an int[] with the numbers that appear in both, each listed once, in ascending order. Each array has between 1 and 1000 items, with values from -1000 to 1000.
For first = [1, 2, 2, 1] and second = [2, 2], the answer is [2]. For first = [4, 9, 5] and second = [9, 4, 9, 8, 4], it is [4, 9]: both numbers occur in both arrays, and although each appears twice in second, the result lists it once.
Approach: remember one array in a set
The brute-force approach checks every item of second against every item of first, which takes O(n × m) time. The slow part is the question "is this number in the first array?", asked once per item of the second.
A HashSet<int> answers that question in constant time on average. It also stores each number once, which takes care of the duplicates in the input. So the plan is:
- Put every number of
firstin a set calledseen. - Go through
second. Each number thatseencontains goes into a second set,intersectionSet. The second set keeps a number once even whensecondrepeats it. - A set has no useful order, so sort its numbers and return them as an array.
Here it is on first = [4, 9, 5] and second = [9, 4, 9, 8, 4]. After the first loop, seen holds 4, 9 and 5. The second loop checks 9 (in seen, so it is added), 4 (added), 9 again (already in intersectionSet, so nothing changes), 8 (not in seen, skipped) and 4 again (nothing changes). The shared numbers are 9 and 4, and sorting them gives [4, 9].
The value range, -1000 to 1000, would also allow a bool[2001] indexed by value + 1000 instead of a set. The set works for any range of integers and doesn't depend on the constraint, so it is the more general tool.
Step by step
- Name the inputs.
int[] nums1 = first;andint[] nums2 = second;are shorter names for the same arrays. Nothing is copied: both names refer to the original array. - Create
seenas an emptyHashSet<int>. It will hold the numbers ofnums1. - Loop over
nums1withforeach (int i in nums1). - Add each number with
seen.Add(i). Adding a number that is already in the set changes nothing, so repeated numbers need no special care. - Create
intersectionSetas a second emptyHashSet<int>. It collects the shared numbers. - Loop over
nums2withforeach (int i in nums2). - Keep the shared numbers. If
seen.Contains(i), additointersectionSet. A number that occurs twice innums2is stored once. - Sort and convert.
intersectionSet.OrderBy(x => x)produces the numbers in ascending order, andToArray()turns the result into anint[], the type the method returns.
The solution in C#
class Solution {
public int[] Intersection(int[] first, int[] second) {
int[] nums1 = first;
int[] nums2 = second;
HashSet<int> seen = new HashSet<int>();
foreach (int i in nums1) {
seen.Add(i);
}
HashSet<int> intersectionSet = new HashSet<int>();
foreach (int i in nums2) {
if (seen.Contains(i)) {
intersectionSet.Add(i);
}
}
return intersectionSet.OrderBy(x => x).ToArray();
}
}
The C# details worth knowing:
- A set is not ordered. The order in which a
HashSethands out its items is not part of its contract, so the code never relies on it. The sort at the end is what guarantees ascending output. Addreturns abool. It givestruewhen the number was new andfalsewhen the set already held it. The lesson ignores the result, because only the final contents matter.OrderByandToArrayare LINQ. They come fromSystem.Linq, andHashSetis inSystem.Collections.Generic. A standalone file needs ausingline for each.OrderBy(x => x)takes a lambda that picks the sort key: here the number itself.- The result is a new array.
ToArray()copies the sorted numbers, so changing the returned array doesn't affect either input. - Reusing the name
i. Both loops declareint i. That is allowed because each variable lives only inside its own loop. - When nothing is shared, the set is empty and the method returns an empty array, not
null.
Complexity
Let n and m be the lengths of first and second, and let u be the number of shared values.
Time: O(n + m + u log u). Building seen takes n additions and scanning second takes m lookups, each constant on average. Sorting the u shared values adds u log u. That is the lesson's O(n + m) plus the time to sort the shared numbers.
Space: O(n + m). seen holds up to n numbers, and intersectionSet and the returned array hold at most min(n, m).
Edge cases and common mistakes
- Duplicates.
[1, 2, 2, 1]and[2, 2]gives[2], not[2, 2]. The second set collapses repeats. - No shared numbers.
[1]and[2]gives[]. - Negative numbers.
[-3, 5, -3, 0]and[0, -3, 7]gives[-3, 0]. They sort like any otherint. - Skipping the sort. The shared numbers would come out in whatever order the set provides, which the code can't depend on. The tests expect ascending order.
- Using a list instead of a set.
List<int>.Containsscans the whole list, which brings the time back toO(n × m). - Returning the
HashSetitself. The method returnsint[], so the set must be converted.
Testing
[1, 2, 2, 1],[2, 2]gives[2].[4, 9, 5],[9, 4, 9, 8, 4]gives[4, 9].[1],[2]gives[].[-3, 5, -3, 0],[0, -3, 7]gives[-3, 0].
