Approach the Problem Using the "trial and error" Algorithm
starting from Leetcode 719 find kth smallest pair distance.
let’s take a look at this problem:
Given an integer array, return the k-th smallest distance among all the pairs. The distance of a pair (A, B) is defined as the absolute difference between A and B.
this problem is easy to understand. and we tried brute force, which is MLE.
and also, for problems like kth smallest/largest, we can use heap as well(PQ).
However, this article will talk about neither of these solutions, instead, we will talk about this trial and error algorithm
think about this: if we sort this given array in ascending order, this problem can be rephrased as finding the kth smallest element in a sorted matrix, where the matrix element at position (i, j) is given by matrix[i][j] = nums[j] - nums[i]. well, looks familiar huh? based on my past experience, this can be solved using PriorityQueue(heap) or Binary Search.
Now, let’s think of this trial and error idea. general speak, every brute force method is kind of naive trial and error algorithm. but sometimes. trial and error idea can lead to a pretty good solution. there are many hard questions that can be solved by trial and error algorithm.
this idea have three steps, they are very simple like DP:
- Construct a candidate solution.
- Verify if it meets our requirements.
- If it does, accept the solution; else discard it and repeat from Step 1.
looks like bullshit, right?
let’s use LC719 as example:
- candidate solution should be mon-negative integer.
- the search space must be [0, max - min], that’s for sure
- verify a given candidate solution: so given a candidate, how can we determine that? we can list every pair distance and sort them and get the kth, but that will MLE. now, let’s think about this: given an integer num, let count(num) denote the number of pairs which distances are no greater than num. so the kth smallest distance will be the smallest integer num that fits count(num) >= k.
Now, we transform the verification process into counting process. but how do we do the counting?
of course we can iterate them, which will cost O(N^2), that’s just as bad as bruteforce. at this time, we may think of presort.
now suppose we have nums sorted. remeber what we need: we need to get the count(num) for any given num. now, based on nums is sorted, so for any fixed i, and can use binary search until we hit the first j(j starting from i) that nums[j] > nums[i] + num. and then we calcuate the length between i and j which is j - i + 1. if this length is not enough to fill k, then i++ and we begin next round. So this verifcation process can be done in O(nlogn).
if we dig deeper, we will find out that this verification can be done in linear time. how? let’s say for previous round(i and j stays the position that nums[j] > nums[i] + num) , and i will increment by 1, now, we still want to find the postion for j that nums[j] > nums[i] + num. but we don’t have to move j back to i+1 like O(nlogn) method, we let j moving forward from its spot. why we can do this? think about it, it’s very simple, because from newi + 1 to oldj, there will be no j that can meet nums[newj] > nums[newi] + num, so newj have to start at oldj.
Now, how can we get num? we know that num has the range of [0, max - min], so if we just tranverse nums and choose the fsrt num such that count(num) >= k, the time complexity will be O(nd), if d is larger than n, the complecity will be pretty bad. (and the following of V which I don’t understand, but “given two integers num1 and num2 such that num1 < num2, we have count(num1) <= count(num2)” is pretty easy to understand, just remeber what count(num) means is alright), let’s just take a look at the solution, it is much clear.
public int smallestDistancePair(int[] nums, int k) {
Arrays.sort(nums);
int n = nums.length;
int l = 0;
int r = nums[n - 1] - nums[0]; //[l, r] is the range of num, means that we can only get num from this range
int cnt = 0;
while (l < r) {
cnt = 0; //count reset to 0 everytime
int m = l + (r - l) / 2; //we don't know what num is, so we binary
for (int i = 0, j = 0; i < n; i++) { //j will be reset to 0 everytime?
while (j < n && nums[j] <= nums[i] + m) j++; //for every i, the j pointer will move to the first postion that nums[j] > nums[i] + m;
cnt += j - i - 1; //count the length
} //after this for loop, cnt is the number of distance that <= m, now we need to compare this cnt to k
if (cnt < k) { //number is not enough, we must increase the choose of num(n) to increase cnt so that it can meet k
l = m + 1; //we have to do this so that l can be the one that can be returned
} else {
r = m;
}
}
return l; // l is indeed the num that makes cnt closest but smaller or equal to k
}
there are other problems that can be solved in this trial and error idea:
-
K-th Smallest Prime Fraction
-
Minimize Max Distance to Gas Station
-
Find K-th Smallest Pair Distance
-
Kth Smallest Number in Multiplication Table
-
Maximum Average Subarray II
-
Kth Smallest Element in a Sorted Matrix

浙公网安备 33010602011771号