Given two integer arrays nums1 and nums2, sorted in non-decreasing order, return their intersection in sorted order.
The intersection must contain each element common to both arrays exactly once.
Example 1
Input: nums1 = [1, 2, 2, 3, 4], nums2 = [2, 2, 4, 6]
Output: [2, 4]
Explanation: The elements 2 and 4 are present in both arrays.
Example 2
Input: nums1 = [1, 3, 5], nums2 = [2, 4, 6]
Output: []
Explanation: There is no common element between both arrays.
Brute Force Approach
The most direct idea is to take each element from nums1 and search the complete nums2 array for the same value.
Finding a match confirms that the element belongs to both arrays. A second search inside the result prevents repeated common values from being added more than once.
Algorithm
Create an empty
intersectionResultto store distinct values that are present in both arrays.Traverse
nums1from left to right and search the completenums2array for the current value to determine whether it belongs to both arrays.If no matching value exists in
nums2, ignore the current element because it cannot be part of the intersection.If a match is found, search
intersectionResultto check whether the value has already been added, preventing duplicate entries.Append the value only when it is not already present. Since
nums1is sorted and processed from left to right, the collected distinct values remain sorted.Return
intersectionResultafter every value ofnums1has been checked.
Dry Run
Intersection of Two Arrays Brute Force Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { vector<int> intersectionResult; for (int value : nums1) { bool foundInSecond = false; // Search for the current value in nums2. for (int candidate : nums2) { if (candidate == value) { foundInSecond = true; break; } } if (!foundInSecond) { continue; } bool alreadyAdded = false; /* * Check whether this common value * is already present in the result. */ for (int storedValue : intersectionResult) { if (storedValue == value) { alreadyAdded = true; break; } } // Add only the first occurrence of a common value. if (!alreadyAdded) { intersectionResult.push_back(value); } } return intersectionResult; }};int main() { vector<int> nums1 = {1, 2, 2, 3, 4}; vector<int> nums2 = {2, 2, 4, 5}; Solution solution; vector<int> answer = solution.intersection(nums1, nums2); for (int value : answer) { cout << value << " "; } return 0;}Complexity Analysis
Time Complexity: O(N × M), where N and M represent the sizes of nums1 and nums2. Each value of nums1 may require scanning the complete nums2 array. Searching the developing result adds at most O(N × R) work, where R ≤ M, so the overall worst-case complexity remains O(N × M).
Space Complexity: O(1) auxiliary space when returned output storage is excluded. The returned intersection requires O(R) space.
Better Approach
Repeatedly scanning nums2 makes the Brute Force Approach expensive. A hash set can remember all distinct values from one array and answer membership questions much faster.
A second set can collect matching values without allowing duplicates. Since hash sets do not preserve sorted order, the collected intersection must be sorted before being returned.
Algorithm
Create
firstSetand insert every value fromnums1, allowing membership checks to be performed in average constant time while automatically ignoring duplicates.Create an empty
intersectionSetto store values confirmed in both arrays without allowing repeated result entries.Traverse
nums2and check whether each current value exists infirstSet.If the value exists, insert it into
intersectionSet, since it is present in both input arrays.Convert
intersectionSetintointersectionResultand sort it because hash-set traversal does not guarantee sorted order.Return
intersectionResultas the distinct sorted intersection.
Dry Run
Intersection of Two Arrays Better Approach Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { unordered_set<int> firstSet; unordered_set<int> intersectionSet; // Store distinct values from the first array. for (int value : nums1) { firstSet.insert(value); } /* * A value belongs to the intersection * only when it also appears in firstSet. */ for (int value : nums2) { if (firstSet.find(value) != firstSet.end()) { intersectionSet.insert(value); } } vector<int> intersectionResult( intersectionSet.begin(), intersectionSet.end() ); // Hash-set order is not sorted. sort(intersectionResult.begin(), intersectionResult.end()); return intersectionResult; }};int main() { vector<int> nums1 = {1, 2, 2, 3, 4}; vector<int> nums2 = {2, 2, 4, 5}; Solution solution; vector<int> answer = solution.intersection(nums1, nums2); for (int value : answer) { cout << value << " "; } return 0;}Complexity Analysis
Time Complexity: O(N + M + R log R) on average, where N and M represent the input sizes and R represents the number of distinct common elements. Building and checking the hash sets requires O(N + M) average time, while sorting the result requires O(R log R) time.
Space Complexity: O(N + R) auxiliary space because firstSet may store every distinct value from nums1, while intersectionSet stores the distinct common values. Building firstSet from the smaller input array can reduce this to O(min(N, M) + R).
Optimal Approach
The sorted order allows both arrays to be examined together. When the current values differ, the smaller value cannot match the current larger value or any later value in the other array.
Moving the pointer at the smaller value safely removes that impossible candidate. Equal values belong to the intersection, so one copy is collected before both pointers move forward.
Algorithm
Create an empty
intersectionResultand initializei = 0andj = 0, where each pointer represents the smallest unprocessed value in its corresponding sorted array.Compare
nums1[i]andnums2[j]while both pointers remain within their arrays.If
nums1[i] < nums2[j], moveiforward because the current value fromnums1is too small to matchnums2[j]or any value appearing after it.If
nums2[j] < nums1[i], movejforward for the same reason.If both values are equal, append the value only when it differs from the last stored result value, then move both pointers because both current occurrences have been processed.
Return
intersectionResultwhen either array is exhausted, since no additional common value can exist after that point.
Dry Run
Intersection of Two Arrays Optimal Approach Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { int n = nums1.size(); int m = nums2.size(); int i = 0; int j = 0; vector<int> intersectionResult; while (i < n && j < m) { // The smaller value cannot match later values. if (nums1[i] < nums2[j]) { i++; } else if (nums1[i] > nums2[j]) { j++; } else { /* * Add the common value only when * it differs from the last result. */ if (intersectionResult.empty() || intersectionResult.back() != nums1[i]) { intersectionResult.push_back(nums1[i]); } i++; j++; } } return intersectionResult; }};int main() { vector<int> nums1 = {1, 2, 2, 3, 4}; vector<int> nums2 = {2, 2, 4, 5}; Solution solution; vector<int> answer = solution.intersection(nums1, nums2); for (int value : answer) { cout << value << " "; } return 0;}Complexity Analysis
Time Complexity: O(N + M), where N and M represent the sizes of nums1 and nums2. Pointer i processes every value from nums1 at most once, while pointer j processes every value from nums2 at most once.
Space Complexity: O(1) auxiliary space when returned output storage is excluded. The returned intersection array requires O(R) space, where R represents the number of distinct common elements.
FAQs
Q1. Why is checking only the final result value enough to remove duplicates in the Optimal Approach?
Both arrays are processed in sorted order, so repeated equal candidates arrive consecutively. Any duplicate match must therefore equal the most recently inserted result value.
Q2. What changes when duplicate occurrences must be preserved in the intersection?
The number of copies added for a value must equal the smaller frequency across both arrays. During two-pointer traversal, every equal pair contributes one copy before both pointers move.
Q3. Can the two-pointer approach work when the input arrays are unsorted?
No. Moving the pointer containing the smaller value is safe only because all later values are at least as large. Unsorted arrays require sorting first or using hash-based frequency or membership structures.
Q4. How can the Better Approach use less auxiliary space?
Store the values of the smaller input array inside the membership set and traverse the larger array. The membership structure then requires O(min(N, M)) space instead of always using O(N) space.
Q5. How can the intersection of more than two sorted arrays be found?
Repeated two-pointer intersection can combine the arrays one at a time. Processing can stop immediately when any intermediate intersection becomes empty.
Be the first to add a comment.