题目名称:和有限的最长子序列
给你一个长度为 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 。
提示:
思路分析
由题意分析注意是子序列,所以不用连续,那么就只要选择nums里面最小的元素来进行组合,所以需要先对nums进行排序 排序完后求前缀和 然后寻找小于等于queries[i]的前缀和 用二分法查找 注意二分法中 l 指向即将插入的位置
首先构建前缀列表pre,需要注意,pre[0]需要是0,以保证queries[i]小于任何nums元素时能返回0。 随后对queries[i]进行右侧边界的二分查找。 右侧边界二分查找需要注意以下内容:
- while (left < right)
- int mid = left + ((right - left) >> 1) + 1;
- right = mid - 1;
- return right;
Code实现
public int[] answerQueries(int[] nums, int[] queries) {
Arrays.sort(nums);
int[] res = new int[queries.length];
int[] pre = new int[nums.length + 1];
pre[0] = 0;
for (int i = 1; i <= nums.length; ++i) {
pre[i] = pre[i - 1] + nums[i - 1];
}
for (int i = 0; i < queries.length; ++i) {
int query = queries[i];
res[i] = binarySearch(pre, query);
}
return res;
}
private int binarySearch(int[] nums, int val) {
int left = 0, right = nums.length - 1;
while (left < right) {
int mid = left + ((right - left) >> 1) + 1;
if (nums[mid] > val) {
right = mid - 1;
} else {
left = mid;
}
}
return right;
}
结果
算法复杂度分析
- 时间复杂度:
- 空间复杂度: