根据 灵茶山艾府 题解所写
题目描述:
给你一个长度为
n
的整数数组nums
,和一个长度为m
的整数数组queries
。返回一个长度为
m
的数组answer
,其中answer[i]
是nums
中 元素之和小于等于queries[i]
的 子序列 的 最大 长度 。子序列 是由一个数组删除某些元素(也可以不删除)但不改变剩余元素顺序得到的一个数组。
示例 1:
输入:nums = [4,5,2,1], queries = [3,10,21] 输出:[2,3,4] 解释:queries 对应的 answer 如下: - 子序列 [2,1] 的和小于或等于 3 。可以证明满足题目要求的子序列的最大长度是 2 ,所以 answer[0] = 2 。 - 子序列 [4,5,1] 的和小于或等于 10 。可以证明满足题目要求的子序列的最大长度是 3 ,所以 answer[1] = 3 。 - 子序列 [4,5,2,1] 的和小于或等于 21 。可以证明满足题目要求的子序列的最大长度是 4 ,所以 answer[2] = 4 。
示例 2:
输入:nums = [2,3,4,5], queries = [1] 输出:[0] 解释:空子序列是唯一一个满足元素和小于或等于 1 的子序列,所以 answer[0] = 0 。
解题思路:
要求子序列的最大长度,子序列中的元素值越小越好
对于本题来说,我们计算的是元素和,所以元素在数组中的位置无关紧要,可以重新排序
将数组从小到大排序后,再从小到大选择尽量多的元素,相当于选择一个前缀,使这些元素的和不超过询问值。
代码实现:
class Solution {public int[] answerQueries(int[] nums, int[] queries) {Arrays.sort(nums);for (int i = 1; i < nums.length; i++) {nums[i] += nums[i - 1]; // 原地求前缀和}for (int i = 0; i < queries.length; i++) {queries[i] = upperBound(nums, queries[i]); // 复用 queries 作为答案}return queries;}// https://www.bilibili.com/video/BV1AP41137w7/// 返回 nums 中第一个大于 target 的数的下标(注意是大于,不是大于等于)// 如果这样的数不存在,则返回 nums.length// 时间复杂度 O(log nums.length)// 采用开区间写法实现private int upperBound(int[] nums, int target) {int left = -1, right = nums.length; // 开区间 (left, right)while (left + 1 < right) { // 区间不为空// 循环不变量:// nums[left] <= target// nums[right] > targetint mid = left + (right - left) / 2;if (nums[mid] > target) {right = mid; // 范围缩小到 (left, mid)} else {left = mid; // 范围缩小到 (mid, right)}}return right;}
}