【刷题之路】LeetCode 2389. 和有限的最长子序列
【刷题之路】LeetCode 2389. 和有限的最长子序列
- 一、题目描述
- 二、解题
- 1、方法——二分法
-
- 1.1、思路分析
- 1.2、代码实现
一、题目描述
原题连接: 2389. 和有限的最长子序列
题目描述:
给你一个长度为 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 。
提示:
n == nums.length
m == queries.length
1 <= n, m <= 1000
1 <= nums[i], queries[i] <= 106
二、解题
1、方法——二分法
1.1、思路分析
由题目描述我们可知,对于每个queries[i],我们都需要找到一个子序列,使得该子序列的元素之和不超过queries[i],
且要使得该子序列的长度最大化,很显然,我们应该尽量选择较小的元素,这样才能使得子序列的长度最大化。
所以我们可以先对nums数组进行排序,然后使用一个数组f来保存nums数组的前缀和,f[i]保存的是数组nums从下标位0到下标位i - 1的元素的和:
所以f数组的长度要比nums数组的长度要大1。
然后我们顺序遍历queries数组,对于每个queries[i],都使用二分查找法在f数组中查找到第一个大于queries[i]的元素,
如果该元素的下标为x,那么x - 1就为对应的最长子序列的长度
1.2、代码实现
有了以上思路,那我们写起代码来也就水到渠成了:
// 先写一个函数,比较两个整数的大小
int cmp_int(const void* p1, const void* p2) {assert(p1 && p2);return *((int*)p1) - *((int*)p2);
}// 再写一个二分查找算法
int binary_search(int left, int right, int* nums, int target) {assert(nums);int mid = 0;while (left < right) {if (nums[left] > target) {return left;}mid = left + (right - left) / 2;if (nums[mid] <= target) {left = mid + 1;}else {right = mid;}}return left;
}int* answerQueries(int* nums, int numsSize, int* queries, int queriesSize, int* returnSize) {assert(nums && queries && returnSize);// 先对nums数组进行排序qsort(nums, numsSize, sizeof(int), cmp_int);int* answer = (int*)malloc(queriesSize * sizeof(int));*returnSize = queriesSize;if (NULL == answer) {perror("malloc");return NULL;}int* f = (int*)malloc((numsSize + 1) * sizeof(int));if (NULL == f) {perror("malloc");return NULL;}int i = 0;int j = 0;int sum = 0;// 求前缀和for (i = 0; i < numsSize + 1; i++) {f[i] = sum;if (i < numsSize) {sum += nums[i];}}for (i = 0; i < queriesSize; i++) {answer[i] = binary_search(0, numsSize + 1, f, queries[i]) - 1;}free(f);f = NULL;return answer;
}
时间复杂度:O((n+m)logn),其中n为数组nums的长度,m为数组queries的长度,对数组nums进行排序的复杂度为nlogn,二分查找的复杂度为logn,故总的时间复杂度为O((n+m)logn)。
空间复杂度:O(n + m)。我们总共需要n + m + 1个额外空间,故空间复杂度为O(n + m)。