设第k小的数为x,表达式count(x){<=x的个数}是关于x的递增函数,条件式enough(x){count(x)>=k}满足二分搜索条件形式[0 0 ... 0 1 1 ...]。

同理,若找第k大的数,设第k大的数为x,表达式count(x){>=x的个数}是关于x的递减函数,条件式enough(x){count(x)<=k}满足二分搜索条件形式[0 0 ... 0 1 1 ...]。

复杂度:T(n)=T(n/2)+f(n),f(n)是条件式的复杂度。根据主方法,看O(1)和O(f(n))哪个阶数高。若f(n)=O(n), T(n)=O(n);若f(n)=O(1), T(n)=O(lgn)。

从行列有序的矩阵中找第k小的数,可用二分法猜结果,或者用最小堆从多个有序行中找。

乘法表中找第k小的数

int findKthNumber(int m, int n, int k) {
    // 猜第k小的数x,x在范围[1..m*n]
    int l = 0, u = m * n + 1;
    while (l + 1 < u) {
        int mid = l + (u - l) / 2;
        if (enough(mid, m, n, k)) {
            u = mid;
        } else {
            l = mid;
        }
    }
    return u;
}

bool enough(int x, int m, int n, int k) {
    // count(x){ <=x的个数 }是关于x的递增函数,
    // enough(x){ count(x)>=k }符合二分搜索的条件形式[0..0 1..1]
    int count = 0;
    for (int r = 1; r <= m; r++) {
        // 乘法表一行行看该行有多少乘积<=x
        count += min(x / r, n);
    }
    return count >= k;        
}

相似:https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix

bool enough(int value, vector<vector<int>>& matrix, int k) {
    int count = 0;
    for (auto &row : matrix) {
        // 看每行<=value的数有多少
        count += upper_bound(row.begin(), row.end(), value) - row.begin();
    }
    return count >= k;
}

数组两数间有距离,找第k小的距离

int smallestDistancePair(vector<int>& nums, int k) {
    // 两数的距离与数的位置无关,先排序数组
    sort(nums.begin(), nums.end());
    // 猜第k小的距离m,m的值范围[0, max(nums)-min(nums)]
    int l = 0, u = nums.back() - nums[0];
    while (l <= u) {
        int mid = l + (u - l) / 2;
        if (enough(mid, nums, k)) {
            u = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    return l;
}

bool enough(int m, vector<int> &nums, int k) {
    // count(m){ <=m的距离个数 }是关于m的递增函数,
    // enough(m){ count(m)>=k }满足二分搜索的条件形式[0..0 1..1]
    int count = 0;
    for (int j = 1; j < nums.size(); j++) {
        // 找 nums[j]-nums[i] <= m,nums[i] >= nums[j]-m
        int i = lower_bound(nums.begin(), nums.end(), nums[j] - m) - nums.begin();
        count += j - i; // 距离对:(i,j),(i+1,j),...,(j-1,j)
    }
    return count >= k;
}

第N小的A,B倍数

int nthMagicalNumber(int N, int A, int B) {
    // 猜第N小的A,B倍数为x,x的取值范围为[min(A,B), N*min(A,B)]
    const int MOD = 1e9 + 7;
    int lcm = A / __gcd(A, B) * B;
    long l = min(A, B), u = (long)N * min(A, B);
    while (l <= u) {
        long mid = l + (u - l) / 2;
        if (enough(mid, A, B, lcm, N)) {
            u = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    return l % MOD;
}

bool enough(long x, int A, int B, int lcm, int N) {
    // count(x){ <=x的A,B倍数的个数}是关于x的递增函数
    // enough(x){ count(x)>=N }满足二分搜索的条件形式[0..0 1..1]
    // 而 count(x) = x/A + x/B - x/lcm(A,B)
    return x / A + x / B - x / lcm >= N;
}