Week 10: Binary Search and Meet-in-the-Middle
Competitive Programming I (Spring 2020)
Ninghui Li (Purdue University)
Binary Search Example 1:
LeetCode: Find the First and Last Position of Element in a Sorted Array
LeetCode: Find the First and Last Position of Element in a Sorted Array
Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value. Your algorithm's runtime complexity must be in the order of O(log n). If the target is not found in the array, return [-1, -1].
Input: nums = [5,7,7,8,8,10], target = 8 Output: [3,4]
Input: nums = [5,7,7,8,8,10], target = 6 Output: [-1,-1]
Find First and Last (C++ Code, no library) 1/2
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> ans(2,-1); // int[] ans = new int[]{-1,-1};
int b = 0, e = nums.size()-1;
if (e == -1 || nums[b]>target || nums[e]<target) return ans;
// invariance: nums[b-1]<target && nums[e]>=target
while (b < e) { // find first occurrence
int m = b + (e-b)/2; // (b+e)/2 may overflow in rare cases
if (nums[m] < target) { b = m+1; }
else if (nums[m]==target && m==b) {
b = m; break; // found m
} else { e = m; }
}
if (nums[b] != target) return ans;
Condition is complicated because we want first appearance.
Necessary for invariance
Performance optimization
Find First and Last (C++ Code, no library) 2/2
e = nums.size() - 1;
ans[0] = b; ans[1] = e;
if (nums[e] == target) return ans;
// invariance: nums[b]==target & nums[e]>target
while (b < e-1) { // find last occurrence
int m = b + (e-b)/2;
if (nums[m] > target) { e = m; }
else { b = m; }
}
ans[1] = b;
return ans;
Suggestion for Writing Binary Search
Find First and Last (C++ Code, using library)
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> ans(2,-1);
if (nums.size() == 0) return ans;
vector<int>::iterator low,up;
// Returns an iterator pointing to the first element that does not compare less than target
low = lower_bound (nums.begin(), nums.end(), target);
if (low == nums.end() || *low != target) return ans;
// Returns an iterator pointing to the first element which compares greater than val
up = upper_bound (low, nums.end(), target);
ans[0] = low - nums.begin();
ans[1] = up - nums.begin() - 1;
return ans;
}
Can we solve this in Java using binarySearch?
Find First and Last (What if Java is Used?)
Binary Search Example Problem 2:
LeetCode: Find Peak Element
LeetCode: Find Peak Element
A peak element is an element that is greater than its neighbors. Given an input array nums, where nums[i] ≠ nums[i+1], find a peak element and return its index. The array may contain multiple peaks; return any one is fine.
Input: nums = [1,2,3,1] Output: 2
Explanation: 3 is a peak element with index 2.
Input: nums = [3,2,1,3,5,6,4] Output: 0 or 5
Find Peak Element Analysis
Lesson on Binary Search
Meet-in-the-Middle Problem 1:
LeetCode: 3 Sum Closest
Warmup
LeetCode: 3 Sum Closest
Given an array nums of n integers 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.
Input: nums = [-1, 2, 1, -4], target = 1 Output: 2
Closest 3sum is 2 = -1 + 2 + 1
3Sum Closest Analysis
C++ Code for 3Sum Closest
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int n = nums.size(), bestgap = INT_MAX, bestsum;
for(auto t=nums.begin(); t<nums.end()-2; ++t) {
auto be = t+1, en = nums.end()-1;
int nt = target - *t;
while (be < en) {
int res = *be + *en, ngap = abs(res - nt);
if (ngap < bestgap) { bestgap = ngap;
bestsum = *be + *en + *t; }
if (res < nt) be++;
else if (res > nt) en--;
else return target;
}
}
return bestsum;
}
}
2
1 2
2 3
3 4
4 5
5 6
100
C++ Code for 3Sum Closest (Another version)
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int n = nums.size(), bestgap = INT_MAX;
for(int i=0; i<n-2; i++) {
int be = i+1, en = n-1;
while (be < en) {
int gap = nums[be] + nums[en] + nums[i] - target;
if (abs(gap) < abs(bestgap)) bestgap = gap;
if (gap < 0) be++;
else if (gap > 0) en--;
else return target;
}
}
return target + bestgap;
}
Meet-in-the-Middle Problem 2:
LeetCode: 4Sum II
LeetCode: 4Sum II
Given four lists A, B, C, D of integer values, compute how many tuples (i, j, k, l) there are such that A[i] + B[j] + C[k] + D[l] is zero. All A, B, C, D have same length of N where 0 ≤ N ≤ 500. All integers are in the range of -228 to 228 - 1.
Input: A = [ 1, 2] B = [-2,-1] C = [-1, 2] D = [ 0, 2]
Output: 2
The two tuples are:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
4Sum II Analysis
4SUM II (Code in Java)
public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
HashMap<Integer,Integer> ab = new HashMap<Integer,Integer>();
for (int x : A) for (int y : B)
ab.merge(x+y, 1, Integer::sum);
int ans = 0;
for (int x : C) for (int y : D)
ans += ab.getOrDefault(-(x+y), 0);
return ans;
}
2SUM to 4SUM
| Exact sum | Nearest sum (strictly harder than exact sum) |
2 SUM | O(N) time O(N) memory Using HashSet Or method for nearest sum. | O(N log N) time O(1) extra memory Sorting plus 2 pointers |
3 SUM | Best method is that for nearest sum. | O(N^2) time O(1) extra memory Sort two arrays. Iterate over the third, 2 pointer over the two sorted arrays |
4 SUM | O(N^2) time O(N^2) memory Store pair sums over 2 arrays in HashSet. | O(N^2 logN) time O(N^2) memory Use two arrays to store pair-wise sums, sort them, and then 2 pointers |
What is the complexity for 5SUM, 6SUM, etc?
Meet-in-the-Middle Example 3:
Maximum Subset Sum
Maximum Subset Sum: Problem
Given a set of n integers where n <= 40. Each of them is at most 1012, find the maximum subset sum less than or equal to S where S <= 1018.
Input : set[] = {38, 34, 18, 12, 6, 5} and S = 42
Output : 41, which is sum of {18, 12, 6, 5}
Bruteforce takes time 240, which is not fast enough. If the n integers are small, one can use dynamic programming, but here numbers are too large.
Maximum Subset Sum: Analysis