LeetCode 第K个最小的素数分数(二分搜索)

一个已排序好的表 A,其包含 1 和其他一些素数. 当列表中的每一个 p

那么第 k 个最小的分数是多少呢? 以整数数组的形式返回你的答案, 这里 answer[0] = p 且 answer[1] = q.

示例:

输入: A = [1, 2, 3, 5], K = 3
输出: [2, 5]
解释:
已构造好的分数,排序后如下所示:
1/5, 1/3, 2/5, 1/2, 3/5, 2/3.
很明显第三个最小的分数是 2/5.
输入: A = [1, 7], K = 1
输出: [1, 7]

注意:

A 的取值范围在 2 — 2000.
每个 A[i] 的值在 1 —30000.
K 取值范围为 1 —A.length * (A.length - 1) / 2

思 路 分 析 : \color{blue}{思路分析:} 刚开始可能大家都会想到先把所以的分数构造出来,然后排序,蛋式这种算法效率非常低。看了下提示,说可以使用优先队列,即优先队列中的元素按照分数值递增的顺序排序,我们构造所有的分数并且维持队列的大小等于k,这样队头即是需要求的结果。

struct cmp {
//自定义排序结构体
	bool operator() (const pair<int, int> & a, const pair<int, int> & b) {
		return a.first*b.second < b.first*a.second;
	}
};

class Solution {
public:
	vector<int> kthSmallestPrimeFraction(vector<int>& A, int K) {
		int Asize = A.size();
		priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> pq;
		//构造所有分数
		for (int i = 0; i < Asize ; ++i) {
			for (int j = 0; j < i; ++j) {
				pair<int, int> temp = make_pair(A[j], A[i]);
				if (pq.size() < K || myCmp(temp, pq.top())) {//维持队列的大小 == k,并且递减的顺序
					pq.push(make_pair(A[j], A[i]));
					if (pq.size() > K) {
						pq.pop();
					}
				}
			}
		}
		//最后队列头部就是第k小的元素
		return {pq.top().first, pq.top().second}
	}
	//比较两个分数的大小
	static bool myCmp (const pair<int, int> & a, const pair<int, int> & b) {
		return a.first * b.second < b.first * a.second;
	}
};

然而超时了,这算法的时间复杂度至少是O(n2)级别。
然而还有一个提示就是使用二分搜索,我们首先确定left = 0.0, right = 1.0,mid = (left + right) / 2,接着我们寻找比mid小的分数个数cnt,如果cnt == k,说明正好找到了,返回p、q即可,如果 cnt < k,说明mid还比较小,这时增大左边界left = mid,否则缩小右边界right = mid。

class Solution {
public:
    vector<int> kthSmallestPrimeFraction(vector<int>& A, int K) {
        double left = 0, right = 1.0, mid;//二分法三个指针
        int p = 0, q = 1, Asize = A.size(), cnt;
        while (true) {
            double mid = (left + right) / 2.0;
            cnt = 0; p = 0;
            //A[i]为分子,A[j]为分母,寻找比mid小的分数个数
            for (int i = 0, j = 0; i < Asize; ++i) {
                while (j < Asize && A[i] > mid * A[j]) {
                    ++j;
                }
                cnt += Asize - j;//以A[i]为分子,比mid小的分数个数
                if (j < Asize && p * A[j] < q * A[i]) {
                    p = A[i];
                    q = A[j];
                }
            }
            if (cnt == K) {//当前mid正好是所寻找的结果
                return {p, q};
            }
            else if (cnt < K) {//mid小了,所以右移left
                left = mid;
            }
            else {//mid大了,所以左移right
                right = mid;
            }
        }
        
    }
};

LeetCode 第K个最小的素数分数(二分搜索)_第1张图片
和前面那几道第k小的处理比较相似,都是使用二分搜索,关键是如果确定mid,以及如何衡量mid的大小。

你可能感兴趣的:(LeetCode,二分法,数学)