leetcode
leetcode 2051 ~ 2100
找出数组的第 K 大和

找出数组的第 K 大和

难度:

标签:

题目描述

代码结果

运行时间: 144 ms, 内存: 28.6 MB


/*
 * 思路:
 * 1. 计算所有可能的子序列和,并将其存储在一个集合中。
 * 2. 使用Java Stream对这些子序列和进行排序,然后获取第 k 大的和。
 */
import java.util.*;
import java.util.stream.Collectors;

public class KthLargestSubsequenceSumStream {
    public int getKthLargestSum(int[] nums, int k) {
        Set<Integer> subsequenceSums = new HashSet<>();
        generateSubsequenceSums(nums, 0, 0, subsequenceSums);
        List<Integer> sortedSums = subsequenceSums.stream()
            .sorted(Comparator.reverseOrder())
            .collect(Collectors.toList());
        return sortedSums.get(k - 1);
    }

    private void generateSubsequenceSums(int[] nums, int index, int currentSum, Set<Integer> subsequenceSums) {
        if (index == nums.length) {
            subsequenceSums.add(currentSum);
            return;
        }
        generateSubsequenceSums(nums, index + 1, currentSum + nums[index], subsequenceSums);
        generateSubsequenceSums(nums, index + 1, currentSum, subsequenceSums);
    }

    public static void main(String[] args) {
        KthLargestSubsequenceSumStream solver = new KthLargestSubsequenceSumStream();
        int[] nums = {2, 4, -2};
        int k = 5;
        System.out.println(solver.getKthLargestSum(nums, k));  // 输出: 2
    }
}

解释

方法:

这道题目的核心思路是利用最小堆来维护所有可能的子序列和,从而找出第 k 大的和。首先,计算所有正数的和 `acc`,这代表了最大的子序列和(即所有正数相加)。然后,使用一个最小堆来维护和操作。堆中的元素是以绝对值排序的,这是因为我们需要逐步从最大的子序列和中减去一些数值来获得次大的和。通过维护堆来迭代地计算可能的子序列和,并逐步获得第 k 大的和。代码中的每一步操作都是基于这个最小堆来进行的,我们通过弹出和推入新的元素来控制和更新堆的状态。

时间复杂度:

O(n log k + k log k)

空间复杂度:

O(k)

代码细节讲解

🦆
为什么在选择绝对值排序而不是直接按原始数值排序来处理子序列和?
在处理子序列和的问题中,选择绝对值排序而非原始数值排序的主要原因是为了确保我们能从最大和中逐步减去的数值是最小的,即逐步探索较小的子序列和。如果我们按原始数值排序,那么负数的处理会变得复杂,因为我们需要考虑加上一个负数实际上是减小总和。而通过绝对值排序,我们可以更直观地按数值大小处理各个数,从而有效地通过最小堆来控制和更新子序列和的大小。
🦆
堆中存储的元素为何要包含索引?这里的索引具体是表示什么?
在堆中存储元素时包含索引是为了追踪当前处理到哪个元素,以及从哪个元素派生出当前的子序列和。这里的索引表示的是当前元素在按绝对值排序后的数组中的位置。通过记录索引,我们可以确保每次操作都是基于正确的元素进行,避免重复处理同一个元素,同时也能按顺序探索所有可能的子序列和。这是一种有效的方式来维护和更新堆中的状态,确保我们能正确地找到第 k 大的子序列和。
🦆
在代码中,如果k等于1,直接返回累加的正数和`acc`,这样做是否考虑到了所有元素都是负数的情况?
在代码中直接返回累加的正数和`acc`如果k等于1确实是一个问题,尤其是当数组中所有元素都是负数时。这种情况下,最大的子序列和应该是0(即不选择任何元素),而不是负数的累加和。因此,代码在处理全负数数组时应该有额外的检查,以确保当k为1且所有数都是负数时返回0。这种情况下的直接返回`acc`实际上是忽略了全负数的特殊情况。
🦆
如果数组中包含重复元素,这种方法是否仍然有效?
如果数组中包含重复元素,使用最小堆来维护和操作子序列和的方法仍然是有效的。重复元素在处理时会被视为独立的实体,因此在计算子序列和时,每个重复的元素都可以被单独考虑。这确保了算法的正确性和完整性,不会因为元素的重复而影响最终的结果。维护最小堆的过程考虑了所有可能的子序列和,包括由重复元素形成的所有组合。

相关问题