This post will be about building intuition for NSums variations of problems.
Update: I will add more variations soon
You may assume that each input would have exactly one solution, and you may not use the same element twice.
You can return the answer in any order.
Example 1:
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].Constraints
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> pair = new HashMap<>();
for(int i = 0; i < nums.length; i++) {
int num = nums[i];
if(pair.containsKey(target - num))
return new int[]{i, pair.get(target - num)};
pair.put(num, i);
}
return new int[]{};
}Constraints:
We will use the sorted property of the array
public int[] twoSum(int[] numbers, int target) {
int low = 0 , high = numbers.length - 1;
while( low < high) {
int sum = numbers[low] + numbers[high];
if(sum < target)
low++;
else if(sum > target)
high--;
else
return new int[]{low + 1, high + 1};
}
return new int[]{-1, -1};
}T: O(n)
S: O(1)
Approach
Optimizations
Boundary checks
Mid Element
class TwoSum {
List<Integer> nums = new ArrayList<>();
Map<Integer, Integer> map = new HashMap<>();
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
public TwoSum() {
nums = new ArrayList<>();
map = new HashMap<>();
}
public void add(int number) {
nums.add(number);
map.put(number, map.getOrDefault(number, 0) + 1);
if(min > number)
min = number;
if(max < number)
max = number;
}
public boolean find(int value) {
//Optimizations
if(value < 2 * min || value > 2 * max)
return false;
if(value % 2 == 0)
if(map.containsKey(value/2) && map.get(value / 2 ) >= 2)
return true;
for(int n : nums) {
int comp = value - n;
if((comp != n && map.containsKey(comp)) || ( comp == n && map.get(comp) > 1) )
return true;
}
return false;
}
}Approach
step 1:
check for the complement in current BST
Step 2:
If the current root is not part of anser, Check for left side and right side of the node
Boolean dfs(TreeNode root, TreeNode curr, int k) {
if( curr == null)
return false;
int complement = k - curr.val;
return search(root, curr, complement) || dfs(root,curr.left, k) || dfs(root,curr.right, k);
}
Boolean search(TreeNode node,TreeNode curr, int k) {
while(node != null) {
if(node != curr && node.val == k)
return true;
if(node.val > k)
node = node.left;
else
node = node.right;
}
return false;
}Approach 2:
void inorder(List<Integer> list, TreeNode root) {
if(root == null)
return;
inorder(list, root.left);
list.add(root.val);
inorder(list, root.right);
}
public boolean findTarget(TreeNode root, int k) {
//Since it is BST, inorder traversal gives ascending order
List<Integer> list = new ArrayList<>();
inorder(list, root);
int left = 0, right = list.size() - 1;
while(left < right) {
int sum = list.get(left) + list.get(right);
if(sum == k)
return true;
if(sum < k)
left++;
else
right--;
}
return false;
}Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]]
such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.
Notice that the solution set must not contain duplicate triplets.Brute Force:
Approach 1:
Variation: 3Sum ( No duplicates using sort + Two Pointers)
Optimization: nums[i] <=0
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums); //nlogn
List<List<Integer>> res = new ArrayList<>();
//Optimization: nums[i] <= 0, if nums[i] is +ve, then we cannot find a result
for(int i = 0; i < nums.length && nums[i] <=0; i++) {
if( i == 0 || nums[i - 1] != nums[i])
twoSums(nums, i, res);
}
return res;
}
void twoSums(int[] nums, int i, List<List<Integer>> res) {
int low = i + 1, hi = nums.length - 1;
while(low < hi ) {
int sum = nums[low] + nums[hi] + nums[i];
if(sum < 0)
low++;
else if (sum > 0)
hi--;
else {
res.add(Arrays.asList(nums[i], nums[low], nums[hi]));
while(low < hi && nums[low] == nums[low +1] )
low++;
while(low < hi && nums[hi] == nums[hi - 1])
hi--;
low++;
hi--;
}
}
}Variation:
Given an array of n integers nums and an integer target, find the number of index triplets i, j, k with 0 <= i < j < k < n that satisfy the condition nums[i] + nums[j] + nums[k] < target.Variation: Once we find a value <= target, all the numbers between left and right would be <=target. So we need to include (right - left) in the final answer
public int threeSumSmaller(int[] nums, int target) {
Arrays.sort(nums);
int sum = 0;
for(int i = 0; i < nums.length - 2; i++) {
sum += twoSumSmaller(nums, i + 1, target - nums[i]);
}
return sum;
}
private int twoSumSmaller(int[] nums, int startIndex, int target) {
int sum = 0;
int left = startIndex;
int right = nums.length - 1;
while(left < right) {
if(nums[left] + nums[right] < target) {
// Everything between right and left would be part of answer
sum += right - left;
left++;
}else
right--;
}
return sum;
}Variation:
Given an integer array nums of length n and an integer target, find three integers in nums such that the sum is closest to target.
Return the sum of the three integers.
You may assume that each input would have exactly one solution.Closest => Absolute distance from target. Keep track of min distance in internal loop.
public int threeSumClosest(int[] nums, int target) {
int closestSum = Integer.MAX_VALUE / 2;
Arrays.sort(nums);
for(int i = 0; i < nums.length - 2 && closestSum != 0; i++) {
int low = i + 1;
int high = nums.length - 1;
//Optimization
int minSum = nums[i] + nums[i + 1] + nums[i + 2];
if( minSum >= target + Math.abs(target - closestSum))
return closestSum;
while(low < high) {
int sum = nums[low] + nums[high] + nums[i];
if(Math.abs(target - sum) < Math.abs(closestSum - target)) {
closestSum = sum;
}
if(sum < target)
low++;
else if(sum > target)
high--;
else
return sum;
}
}
return closestSum;
}Given an array nums of n integers, return an array of all the unique quadruplets [nums[a], nums[b], nums[c], nums[d]] such that:
0 <= a, b, c, d < n
a, b, c, and d are distinct.
nums[a] + nums[b] + nums[c] + nums[d] == targetIn this problem, we have 4 variables - a, b, c, d.
So lets generalize the solution to n variables.
Template
k - variables, targetSum
- Sort
- Boundary Checks
- Outer loop
- Optimization
- k - 1 loops
- 2Sum public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
return kSum(nums, target, 0, 4);
}
public List<List<Integer>> kSum(int[] nums, long target, int start, int k) {
List<List<Integer>> res = new ArrayList<>();
if(start == nums.length)
return res;
//Optimization
long avg_value = target / k;
if(nums[start] > avg_value || avg_value > nums[nums.length - 1])
return res;
if(k == 2)
return twoSum(nums, target, start);
for(int i = start; i < nums.length; i++) {
if( i == start || nums[i- 1] != nums[i])
for(List<Integer> subset: kSum(nums, (long) target - nums[i], i + 1, k -1)) {
res.add(new ArrayList<>(Arrays.asList(nums[i])));
res.get(res.size() - 1).addAll(subset);
}
}
return res;
}
public List<List<Integer>> twoSum(int[] nums, long target, int start) {
List<List<Integer>> res = new ArrayList<>();
Set<Long> seen = new HashSet();
int left = start, right = nums.length - 1;
while(left < right) {
long sum = (long) nums[left] + (long) nums[right];
if(sum == target) {
res.add(Arrays.asList(nums[left], nums[right]));
while(left < right && nums[left] == nums[left + 1]) left++;
while(left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
}else if(sum < target)
left++;
else
right--;
}
return res;
}