【刷题之路】LeetCode 2389. 和有限的最长子序列

【刷题之路】LeetCode 2389. 和有限的最长子序列

一、题目描述

原题连接: 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)。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林先生-1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值