Start learning free

LESSON 277 OF 442 · HASH TABLE

Intersection of Two Arrays in TypeScript: Hash Set in O(n + m)

Practice O(n + m) Read the full explanation

Given two integer arrays first and second, return an array of their intersection. Each element in the result must be unique, and the result must be sorted in ascending order.

 

Example 1:

Input: first = [1,2,2,1], second = [2,2]
Output: [2]

Example 2:

Input: first = [4,9,5], second = [9,4,9,8,4]
Output: [4,9]
Explanation: 4 and 9 are in both arrays. Each appears twice in second, but only once in the result.

 

Constraints:

  • 1 <= first.length, second.length <= 1000
  • -1000 <= first[i], second[i] <= 1000
Note:

Your algorithm should run in O(n + m) time, plus the time to sort the shared numbers, where n and m are the lengths of first and second.

STEP 1 OF 10
    Ctrl + Enter to check

    TYPESCRIPT · HASH TABLE

    Intersection Of Two Arrays in TypeScript, explained

    Problem overview

    The method intersection receives two arrays of integers, first and second. It returns the numbers that appear in both, each exactly once, sorted from smallest to largest.

    For first = [1, 2, 2, 1] and second = [2, 2] the answer is [2]: only 2 is shared, and it appears once in the result however often it repeats. For first = [4, 9, 5] and second = [9, 4, 9, 8, 4] the answer is [4, 9]. Both 4 and 9 are in both arrays, and the 5 and the 8 each belong to one array only.

    The lesson asks for O(n + m) time, plus the sort of the shared numbers, where n and m are the lengths of the two arrays.

    Approach: a set answers "is it in the other array?"

    The direct way is to take each number of first and scan second for it. That is O(n × m), because each question "is this number in the other array?" costs a whole scan.

    A Set answers that question in constant time on average. Put every number of second into a set once. Then walk through first and, for each number, ask the set. Only numbers present in both arrays pass the test.

    That leaves the duplicates. If the number 2 appears twice in first, it must be added to the answer only once. The trick is to delete a number from the set the moment it is added to the answer. Its repeats then fail the test, because the set no longer contains it. Adding repeated values to a set changes nothing, so duplicates in second need no care at all.

    Finally, the numbers come out in the order of first, not in numeric order, so a sort finishes the job. On first = [4, 9, 5] the matching numbers are found in the order 4, 9, which happens to be sorted, but first = [9, 4] would give [9, 4] before sorting.

    Step by step

    1. Give the inputs shorter names: nums1 and nums2. This copies nothing; both names point to the same arrays as first and second.
    2. Create the empty answer array ans, typed number[]. The shared numbers will be pushed onto it.
    3. Create an empty Set<number> named set. It will hold the numbers of nums2.
    4. Write a loop over the indexes of nums2. The whole of nums2 must be in the set before nums1 is examined.
    5. Add nums2[i] to the set. A number that is already there is ignored, so repeats take care of themselves.
    6. Write a second loop over the indexes of nums1, using j.
    7. Test set.has(nums1[j]). It is true only for numbers that occur in nums2.
    8. Push nums1[j] onto ans inside the if: this number is in both arrays.
    9. Delete nums1[j] from the set right after. Later copies of the same number in nums1 now fail the test, so each shared number is added once.
    10. Sort ans and return it, with ans.sort((a, b) => a - b).

    The solution in TypeScript

    class Solution {
        intersection(first: number[], second: number[]): number[] {
            let nums1 = first, nums2 = second;
            let ans: number[] = [];
            let set = new Set<number>();
            for (let i = 0; i < nums2.length; i++) {
                set.add(nums2[i]);
            }
            for (let j = 0; j < nums1.length; j++) {
                if (set.has(nums1[j])) {
                    ans.push(nums1[j]);
                    set.delete(nums1[j]);
                }
            }
            ans.sort((a, b) => a - b);
            return ans;
        }
    }
    

    The TypeScript details worth a closer look:

    • Set<number>. A Set stores unique values. The type argument is erased at run time, and it only helps the compiler check what you add. add, has and delete take constant time on average.
    • The comparator is required. Without one, sort() compares elements as strings, so [10, 9, 1, -5].sort() gives [-5, 1, 10, 9]. With (a, b) => a - b, a negative result puts a first, giving numeric order: [10, 9, 1, -5] becomes [-5, 1, 9, 10]. Constraints allow negative numbers and numbers with different digit counts, so both cases matter.
    • sort works in place. It reorders ans and returns the same array, which is why the next line can return ans.
    • Aliasing, not copying. let nums1 = first makes a second name for the same array. That is fine here because neither array is modified.
    • let versus const. Nothing here is reassigned, so const would be the more idiomatic choice. The lesson's code works the same with let.

    Complexity

    Let n and m be the lengths of first and second, and r the number of shared values.

    Time: O(n + m + r log r). Filling the set takes m constant-time additions, the second loop takes n constant-time checks, and the final sort handles only the r shared values. Since r is never larger than the smaller array, this is within the lesson's O(n + m) plus the sort.

    Space: O(m + r). The set holds up to m distinct numbers and the answer holds r.

    Edge cases and common mistakes

    • No shared numbers. first = [1, 2] with second = [3] gives an empty array, [].
    • Repeats in first. In the first example, the 2 matches once, is deleted from the set, and is skipped on its repeat. Without the delete the answer would be [2, 2].
    • Repeats in second. They make no difference, because adding a present value to a set does nothing.
    • Forgetting the comparator. [10, 9] would stay as [10, 9] with default sorting, because the text "10" sorts before "9".
    • Negative numbers. A set treats -5 like any other number. The comparator also puts negatives first, so first = [10, 9, -5] and second = [9, 10, -5] give [-5, 9, 10].
    • Using includes on the array. nums2.includes(x) works, but scans the array each time and brings back the O(n × m) cost.

    Testing

    • first = [1, 2, 2, 1], second = [2, 2] gives [2].
    • first = [4, 9, 5], second = [9, 4, 9, 8, 4] gives [4, 9].
    • first = [1, 2], second = [3] gives [].
    • first = [10, 9, -5], second = [9, 10, -5] gives [-5, 9, 10].
    Keep going with TypeScriptAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free