通用方法:
快排/优先队列
多路归并:使用[哈希表]和优先队列找到K个最小的数即可!
二分:二分目标值统计小于等于该值的元素个数,正好为K个时返回其中最大的元素。
786. 第 K 个最小的素数分数
给你一个按递增顺序排序的数组 arr 和一个整数 k 。数组 arr 由 1 和若干 素数 组成,且其中所有整数互不相同。
对于每对满足 0 < i < j < arr.length 的 i 和 j ,可以得到分数 arr[i] / arr[j] 。
那么第 k 个最小的分数是多少呢? 以长度为 2 的整数数组返回你的答案, 这里 answer[0] == arr[i] 且 answer[1] == arr[j] 。
示例 1:
输入:arr = [1,2,3,5], k = 3 输出:[2,5] 解释:已构造好的分数,排序后如下所示: 1/5, 1/3, 2/5, 1/2, 3/5, 2/3 很明显第三个最小的分数是 2/5
示例 2:
输入:arr = [1,7], k = 1 输出:[1,7]
提示:
2 <= arr.length <= 1000
1 <= arr[i] <= 3 * 104
arr[0] == 1
arr[i]
是一个 素数 ,i > 0
arr
中的所有数字 互不相同 ,且按 严格递增 排序1 <= k <= arr.length * (arr.length - 1) / 2
思路:
方法一:多路归并
使用优先队列找到K个最小的数,返回第K个最小的数
方法二:二分
二分答案X,找到小于等于X的所有元素,恰为K个时返回其中最大的一个即可。
// 多路归并
class Solution {
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
PriorityQueue<int[]> q = new PriorityQueue<>((o1, o2) -> (arr[o1[0]] * arr[o2[1]] - arr[o2[0]] * arr[o1[1]]));
for (int i = 1; i < arr.length; i++)
q.offer(new int[]{0, i});
int[] res = new int[2];
while (k-- > 0) {
// System.out.println(q);
int[] p = q.poll();
res[0] = arr[p[0]];
res[1] = arr[p[1]];
if (p[0] + 1 < p[1])
q.offer(new int[]{p[0] + 1, p[1]});
}
return res;
}
}
// 二分
class Solution {
public int[] kthSmallestPrimeFraction(int[] arr, int k) {
double l = 0, r = 1.0;
int x = 0, y = 1;
while (true) {
double mid = (l + r) / 2;
int cnt = 0;
x = 0;
y = 1;
for (int j = -1, i = 1; i < arr.length; i++) {
while (1.0 * arr[j + 1] / arr[i] < mid) {
j++;
if (x * arr[i] < y * arr[j]) {
x = arr[j];
y = arr[i];
}
}
cnt += j + 1;
}
if (cnt == k)
return new int[]{x, y};
if (cnt < k)
l = mid;
else
r = mid;
}
}
}
440. 字典序的第K小数字
给定整数 n 和 k,返回 [1, n] 中字典序第 k 小的数字。
示例 1:
输入: n = 13, k = 2 输出: 10 解释: 字典序的排列是 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9],所以第二小的数字是 10。
示例 2:
输入: n = 1, k = 1 输出: 1
提示:
- 1 <= k <= n <= 109
思路:
数位统计问题
考虑前缀出现的次数,从前往后确定每一位
class Solution {
public int findKthNumber(int n, int k) {
int prefix = 1;
while (k > 1) {
int cnt = find(prefix, n);
if (cnt >= k) {
prefix *= 10;
k--;
} else {
prefix++;
k -= cnt;
}
}
return prefix;
}
int find(int prefix, int n) {
String s = String.valueOf(n), t = String.valueOf(prefix);
long p = 1;
int len = s.length() - t.length();
long res = 0;
for (int i = 0; i < len; i++) {
res += p;
p *= 10;
}
s = s.substring(0, t.length());
if (s.compareTo(t) > 0) {
res += p;
} else if (s.compareTo(t) == 0) {
res += n - prefix * p + 1;
}
return (int)(res);
}
}
215. 数组中的第K个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2 输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4 输出: 4
提示:
- 1 <= k <= nums.length <= 104
- -104 <= nums[i] <= 104
思路:
优先队列/快速排序
// 优先队列
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> q = new PriorityQueue<>();
for (int x : nums) {
q.offer(x);
if (q.size() > k)
q.poll();
}
return q.poll();
}
}
// 快速排序
class Solution {
public int findKthLargest(int[] nums, int k) {
int n = nums.length;
return quickSort(nums, 0, n - 1, k - 1);
}
int quickSort(int[] nums, int l, int r, int k) {
if (l == r)
return nums[l];
int x = nums[l + r >> 1];
int i = l - 1, j = r + 1;
while (i < j) {
while (nums[++i] > x);
while (nums[--j] < x);
if (i < j) {
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
}
}
if (j >= k)
return quickSort(nums, l, j, k);
else
return quickSort(nums, j + 1, r, k);
}
}
面试题 17.14. 最小K个数
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4 输出: [1,2,3,4]
提示:
- 0 <= len(arr) <= 100000
- 0 <= k <= min(100000, len(arr))
思路:
快排/优先队列
// 优先队列
class Solution {
public int[] smallestK(int[] arr, int k) {
PriorityQueue<Integer> q = new PriorityQueue<>((o1, o2) -> o2.compareTo(o1));
for (int x : arr) {
q.offer(x);
if (q.size() > k)
q.poll();
}
int[] res = new int[k];
for (int i = k - 1; i >= 0; i--)
res[i] = q.poll();
return res;
}
}
// 快排
class Solution {
public int[] smallestK(int[] arr, int k) {
int n = arr.length;
quickSort(arr, 0, n - 1, k - 1);
int[] res = new int[k];
for (int i = 0; i < k; i++)
res[i] = arr[i];
return res;
}
void quickSort(int[] q, int l, int r, int k) {
if (l >= r)
return;
int x = q[l + r >> 1], i = l - 1, j = r + 1;
while (i < j) {
while (q[++i] < x);
while (q[--j] > x);
if (i < j) {
q[i] ^= q[j];
q[j] ^= q[i];
q[i] ^= q[j];
}
}
if (k <= j)
quickSort(q, l, j, k);
else
quickSort(q, j + 1, r, k);
}
}