1. Introduction
In this tutorial, we’ll explain how to find common elements in two sorted arrays.
2. Common Elements of Two Sorted Arrays
In this problem, we have two sorted arrays: and . Our task is to find the common elements.
For instance, if and , our algorithm should output as the result.
To find the common elements efficiently, it should use the fact that and are already sorted. Additionally, we’ll also require the output array to be non-descending as the input arrays.
We’ll also assume that and can contain duplicates. So, if a common element is repeated twice in , and thrice in , we’ll include it twice in the result array. For instance:
3. Finding Common Elements in Linear Time
Let’s start with the naive algorithm that doesn’t use the fact that and are sorted:
algorithm NaiveFindCommonElements(a, b):
// INPUT
// a = a sorted array with n elements
// b = a sorted array with m elements
// OUTPUT
// c = the sorted array of the common elements of a and b
c <- an empty array
for i <- 1 to n:
for j <- 1 to m:
if a[i] = b[j]:
// We found a common element
append a[i] to c
return c
For each , the algorithm iterates over the entire to check if . So, it has an time complexity, which implies if and are comparable. What can we change in this algorithm to get common elements faster?
3.1. Linear Search
Well, since the arrays are sorted, there’s no point in iterating over if . Each of those is greater than , so we can conclude that . The converse is also true: if , we can discard .
That way, we iterate over each array only once. So, we don’t need nested loops. Instead, we can start with and . When , we discard by incrementing . Similar goes for if . If , we append to the result array and update both counters. The loop stops once we reach the end of or . This approach is similar to the merge step in the Merge Sort algorithm:
algorithm LinearAlgorithmForCommonElements(a, b):
// INPUT
// a = a sorted array with n elements
// b = a sorted array with m elements
// OUTPUT
// c = the sorted array of the common elements of a and b
c <- an empty array
i <- 1
j <- 1
while i <= n and j <= m:
if a[i] < b[j]:
i <- i + 1
else if b[j] < a[i]:
j <- j + 1
else: // We've got a match: a[i] = b[j]!
append a[i] to c
i <- i + 1
j <- j + 1
return c
As a result, the algorithm’s time complexity is linear: .
3.2. Example
Let’s use the above example to show how the algorithm works.
Initially, is empty, and both and point to the first elements:
Since , we increment :
Now, we have a match, so we append 2 to and increment both counters:
We have a match again, so we do the same as in the previous step:
Now, , so we discard and move on to :
The situation is the same (), so we increment only :
Since , we increment only :
We found a new common element, so we add it to and increment both counters:
Finally, , so we stop and output the result: .
4. Finding Common Elements in Logarithmic Time
Let’s suppose that . Then, for each , we can look for in with the logarithmic binary search. Since , if the binary search finds at the -th position in , we can discard all the element to the left and search for in the remainder: .
Similarly, let’s say the binary search doesn’t find in , but the last index it checks in is . If , we search for in . On the other hand, if , it can still happen that , so we don’t discard .
4.1. Pseudocode
Here’s how the above algorithm works:
algorithm FindCommonElementsBinarySearch(a, b):
// INPUT
// a = a sorted array with n elements
// b = a sorted array with m elements
// OUTPUT
// c = the sorted array of the common elements of a and b
c <- an empty array
k <- 1
j <- 1
while j <= m and k <= n:
// Find the position b[j] should be at in a[k:n]
k <- BINARY-SEARCH(b[j], a[k:n])
if b[j] = a[k]:
append b_j to c
k <- k + 1
else:
if j < m and b[j+1] > b[j]:
k <- k + 1
j <- j + 1
return c
The worst-case time complexity is . If , we can consider to be a constant with respect to . In that case, the algorithm has an approximate complexity. However, if , we get an algorithm. That’s still better than the naive approach but worse than the runtime of the two-side linear search.
5. Conclusion
In this article, we presented two efficient ways of finding common elements of two sorted arrays. One approach has an complexity, whereas the other combines binary and linear searches and runs in an time.