Problem: 2386. 找出数组的第 K 大和
文章目录
- 思路
- 复杂度
- 💖 小根堆
- 💖 TODO:二分 + 暴搜
思路
👨🏫 灵神题解
复杂度
时间复杂度:
添加时间复杂度, 示例: O ( n ) O(n) O(n)
空间复杂度:
添加空间复杂度, 示例: O ( n ) O(n) O(n)
💖 小根堆
class Solution {
class Pair
{
long sum;
int idx;
public Pair(long x, int y)
{
super();
this.sum = x;
this.idx = y;
}
}
public long kSum(int[] nums, int k)
{
long sum = 0;
int n = nums.length;
for (int i = 0; i < n; i++)
{
if (nums[i] >= 0)
sum += nums[i];
else
nums[i] = -nums[i];
}
Arrays.sort(nums);
PriorityQueue<Pair> heap = new PriorityQueue<>((a, b) -> Long.compare(a.sum, b.sum));
heap.offer(new Pair(0L, 0));// 空子序列
// 一个不选也是一种情况
while (--k > 0)// 注意:--k 比 k-- 要少一次循环
{
Pair p = heap.poll();
long s = p.sum;
// System.out.print(s + " "); //调试输出
int i = p.idx;
if (i < n)
{
// 在子序列末尾添加 nums[i]
heap.offer(new Pair(s + nums[i], i + 1));// 下一个要添加的元素下标为 i+1
if (i > 0)// 替换子序列末尾元素为 nums[i]
heap.offer(new Pair(s + nums[i] - nums[i - 1], i + 1));
}
}
// heap.peek().sum 是第k小
// sum 是第 1 大
return sum - heap.peek().sum;
}
}
💖 TODO:二分 + 暴搜
class Solution {
public long kSum(int[] nums, int k) {
long sum = 0, right = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] >= 0) {
sum += nums[i];
} else {
nums[i] = -nums[i];
}
right += nums[i];
}
Arrays.sort(nums);
long left = -1;
while (left + 1 < right) { // 开区间二分,原理见【前置知识】
long mid = (left + right) / 2;
cnt = k - 1; // 空子序列算一个
dfs(0, mid, nums);
if (cnt == 0) { // 找到 k 个元素和不超过 mid 的子序列
right = mid;
} else {
left = mid;
}
}
return sum - right;
}
private int cnt;
// 反向递归,增加改成减少,这样可以少传一些参数
private void dfs(int i, long s, int[] nums) {
if (cnt == 0 || i == nums.length || s < nums[i]) {
return;
}
cnt--;
dfs(i + 1, s - nums[i], nums); // 选
dfs(i + 1, s, nums); // 不选
}
}
// 作者:灵茶山艾府