Difficulty: Medium, Asked-in: Google, Facebook, Microsoft, Amazon, Morgan Stanley.
Key takeaway:
Given an array of n integers and a value targetSum, write a program to check whether there is a pair of elements in the array whose sum is equal to targetSum. If yes, return true; otherwise, return false.
Input: X[] = [-5, 1, -40, 20, 6, 8, 7], targetSum = 15, Output: true
Explanation: (7, 8) or (-5, 20) are the pairs with the sum of 15.
Input: X[] = [-5, 4, -2, 16, 8, 9], targetSum = 15, Output: false
Explanation: There is no pair of elements whose sum is equal to 15.
One basic solution is to check each possible pair of elements. If there exists a pair (i, j) with a sum equal to targetSum, we return true. To achieve this: We run two nested loops to explore all possible pairs: the outer loop from i = 0 to n - 2 and the inner loop from j = i + 1 to n - 1.
Inside the inner loop, we check if the sum of the current pair (X[i] and X[j]) is equal to the targetSum. If it is, we return true. By the end of the nested loops, there is no such pair in the array and we return false.
There are two critical questions:
bool checkPairSum(int X[], int n, int targetSum)
{
for (int i = 0; i < n - 1; i = i + 1)
{
for (int j = i + 1; j < n; j = j + 1)
{
if (X[i] + X[j] == targetSum)
return true;
}
}
return false;
}
def checkPairSum(X, n, targetSum):
for i in range(n - 1):
for j in range(i + 1, n):
if X[i] + X[j] == targetSum:
return True
return False
We are exploring all possible pairs in the array and doing constant operations for each pair. Total number of possible pairs = nC2 = n(n - 1)/2 = O(n²). So time complexity = O(n²). We are using constant extra space, so space complexity = O(1).
The critical question is: How can we improve the time complexity? On a sorted array, binary search will take O(log n) time. Can we solve this problem using sorting and binary search? Let's think!
For every element X[i], if targetSum - X[i] is present in the array, then there exists a pair with targetSum. So one idea is to sort the array and apply binary search to find targetSum - X[i] for every element X[i]. If targetSum - X[i] is present, we return true.
bool binarySearch(int X[], int l, int r, int key)
{
while (l <= r)
{
int mid = l + (r - l) / 2;
if (X[mid] == key)
return true;
if (X[mid] < key)
l = mid + 1;
else
r = mid - 1;
}
return false;
}
bool checkPairSum(int X[], int n, int targetSum)
{
sort(X, X + n);
for(int i = 0; i < n; i = i + 1)
{
int k = binarySearch(X, 0, n - 1, targetSum - X[i]);
if (k == true)
return true;
}
return false;
}
def binary_search(X, l, r, key):
while l <= r:
mid = l + (r - l) // 2
if X[mid] == key:
return True
elif X[mid] < key:
l = mid + 1
else:
r = mid - 1
return False
def check_pair_sum(X, n, target_sum):
X.sort()
for i in range(n):
k = binary_search(X, 0, n - 1, target_sum - X[i])
if k:
return True
return False
Suppose we are using the O(nlogn) sorting algorithm heap sort and iterative binary search for the implementation. Time complexity = Time complexity of heap sort + n * Time complexity of binary search = O(nlogn) + n* O(logn) = O(nlogn) + O(nlogn) = O(nlogn).
Space complexity = Space complexity of heap sort + Space complexity of the iterative binary search = O(1) + O(1) = O(1).
Can we optimize the above approach further? Instead of applying binary search, can we think some different method to search for a pair in the sorted array? Let's explore!
On a sorted array, sometimes two pointers approach works perfectly. Here is a thought process: Suppose we sort the array and walk two pointers inward from both ends of the array, looking at their sum at each point.
Step 1: We sort the array in increasing order.
Step 2: Now we initialize two pointers: l = 0 and r = n - 1.
Step 3: Next, we run a while loop till l < r.
Step 4: By the end of the loop, if we haven't found such a pair in the entire array, we return false.
bool checkPairSum(int X[], int n, int targetSum)
{
sort(X, X + n);
int l = 0, r = n - 1;
while (l < r)
{
if (X[l] + X[r] == targetSum)
return true;
else if (X[l] + X[r] < targetSum)
l = l + 1;
else
r = r - 1;
}
return false;
}
def check_pair_sum(X, n, target_sum):
X.sort()
l = 0
r = n - 1
while l < r:
if X[l] + X[r] == target_sum:
return True
elif X[l] + X[r] < target_sum:
l = l + 1
else:
r = r - 1
return False
Suppose we use an in-place O(n log n) sorting algorithm like heap sort. The critical question is: What would be the time complexity of the while loop? Here's an idea: Based on the comparison, we either move the left or right pointer by 1. In the worst case, we will access each array element using the while loop. So the time complexity of the while loop is O(n).
Overall time complexity = Time complexity of heap sort + Time complexity of the while loop = O(n log n) + O(n) = O(n log n). In the code, we are using constant extra space because heap sort is an in-place sorting algorithm. So space complexity is O(1).
In the last two solutions, the time complexity is still in the range of O(nlogn) due to the dominance of the sorting algorithm. The critical question is: Can we optimize further to solve it using linear time complexity? If we observe the above solutions, searching is a critical operation. So one idea would be to use a hash table because the hash table performs searching efficiently in the O(1) average.
Here is an idea: We iterate over each array element and insert element X[i] into the Hash table. Before inserting X[i], we check if the value targetSum - X[i] already exists in the table. If it exists, we have found a pair with a sum equal to targetSum.
bool checkPairSum(int X[], int n, int targetSum)
{
unordered_set<int> hashTable;
for (int i = 0; i < n; i = i + 1)
{
int k = targetSum - X[i];
if (hashTable.find(k) != hashTable.end())
return true;
hashTable.insert(X[i]);
}
return false;
}
def checkPairSum(X, n, targetSum):
hashTable = set()
for i in range(n):
k = targetSum - X[i]
if k in hashTable:
return True
hashTable.add(X[i])
return False
public class PairSumChecker
{
public static boolean checkPairSum(int[] X, int n, int targetSum)
{
HashSet<Integer> hashTable = new HashSet<>();
for (int i = 0; i < n; i = i + 1)
{
int k = targetSum - X[i];
if (hashTable.contains(k))
return true;
hashTable.add(X[i]);
}
return false;
}
}
In the worst case, we need to scan the whole array to find such a pair. So time complexity = Time complexity of inserting elements into the hash table + Time complexity of searching targetSum - X[i] into the hash table = n * O(1) + n * O(1) = O(n) + O(n) = O(n). Space complexity = O(n), as we are using a hash table of size O(n).
Please write in the message below if you find anything incorrect, or if you want to share more insight. Enjoy learning, Enjoy algorithms!