文章目录
- 题目描述与示例
- 题目描述
- 输入描述
- 输出描述
- 示例一
- 输入
- 输出
- 说明
- 示例二
- 输入
- 输出
- 解题思路
- 代码
- Python
- Java
- C++
- 时空复杂度
- 华为OD算法/大厂面试高频题算法练习冲刺训练
题目描述与示例
题目描述
给定一个含有N
个正整数的数组,求出有多少个连续区间(包括单个正整数),它们的和大于等于x
。
输入描述
第一行两个整数N
x
(0 < N <= 100000
,0 <= x <= 10000000
)
第二行有N
个正整数(每个正整数小于等于100
)。
输出描述
输出一个整数,表示所求的个数。
示例一
输入
3 7
3 4 7
输出
4
说明
3+4
4+7
3+4+7
7
这四组数据都是大于等于7
的,所以答案为4
示例二
输入
10 10000000
1 2 3 4 5 6 7 8 9 10
输出
0
解题思路
题目要求求连续区间的和,很容易想到使用前缀和的思路来解决。先求出前缀和数组,如对示例一求出[3, 4, 7]
的前缀和数组为pre_sum_list = [0, 3, 7, 14]
。
由于原数组中每一个元素均为非负整数,故前缀和数组一定是一个非递减数组。
对于pre_sum_list
中的每一个元素pre_sum_list[i]
,我们要找到索引j
(i < j
)。
j
是满足pre_sum_list[j] - pre_sum_list[i] >= target
的第一个索引。- 此时任意比
j
大的索引k
,都能使得pre_sum_list[k] - pre_sum_list[i] >= target
成立,即区间[i,k]
是一个符合要求的区间。 - 由于前缀和数组是一个非递减数组,
j
的寻找可以很容易地使用二分查找来完成。 - 假设
j
已经求出,那么对于i
而言,一共存在n+1-j
个这样的区间,满足连续区间和大于等于target
。将所有i
对应的n+1-j
计算并求和,即可以得到答案。
代码
Python
# 题目:【前缀和】2023B-寻找连续区间
# 分值:100
# 作者:闭着眼睛学数理化
# 算法:前缀和/二分查找
# 代码有看不懂的地方请直接在群上提问from itertools import accumulaten, target = map(int, input().split())
nums = list(map(int, input().split()))
# 构建前缀和数组
pre_sum_list = [0] + list(accumulate(nums))
# 初始化答案
ans = 0# 只需要遍历前n个前缀和即可,最后一个前缀和无需考虑
for i in range(n):# 二分查找的目标值bi_target = pre_sum_list[i] + target# 优化:如果发现二分目标值已经超过了数组中所有元素总和# 则必然无法找到以i为起始位置的连续区间,其和大于target# 故直接退出循环即可if bi_target > pre_sum_list[-1]:break# 左闭右开区间# 注意前缀和数组的长度为n+1,故此处right初始化为n+2left, right = i, n+2while left < right:mid = left + (right - left) // 2# 找到第一个大于等于bi_target的数的索引if pre_sum_list[mid] >= bi_target:right = midelse:left = mid + 1# 退出while循环时,j = left = right# 是使得pre_sum_list[j] >= bi_target的第一个索引# 一共有n+1-j个区间,其区间和大于targetans += (n+1-left)print(ans)
Java
import java.util.Scanner;
import java.util.Arrays;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int target = scanner.nextInt();int[] nums = new int[n];for (int i = 0; i < n; i++) {nums[i] = scanner.nextInt();}int[] preSum = new int[n + 1];preSum[0] = 0;for (int i = 0; i < n; i++) {preSum[i + 1] = preSum[i] + nums[i];}int ans = 0;for (int i = 0; i < n; i++) {int biTarget = preSum[i] + target;if (biTarget > preSum[n]) {break;}int left = i;int right = n + 2;while (left < right) {int mid = left + (right - left) / 2;if (preSum[mid] >= biTarget) {right = mid;} else {left = mid + 1;}}ans += (n + 1 - left);}System.out.println(ans);}
}
C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;int main() {int n, target;cin >> n >> target;vector<int> nums(n);for (int i = 0; i < n; i++) {cin >> nums[i];}vector<int> preSum(n + 1, 0);for (int i = 0; i < n; i++) {preSum[i + 1] = preSum[i] + nums[i];}int ans = 0;for (int i = 0; i < n; i++) {int biTarget = preSum[i] + target;if (biTarget > preSum[n]) {break;}int left = i;int right = n + 2;while (left < right) {int mid = left + (right - left) / 2;if (preSum[mid] >= biTarget) {right = mid;} else {left = mid + 1;}}ans += (n + 1 - left);}cout << ans << endl;return 0;
}
时空复杂度
时间复杂度:O(NlogN)
。构建前缀和的时间复杂度为O(N)
。对于前缀和数组中的每一个元素pre_sum_list[i]
,都要进行二分查找,单次二分查找的时间复杂度为O(logN)
,一共需要进行N
次二分查找。
空间复杂度:O(N)
。前缀和数组所占空间。
华为OD算法/大厂面试高频题算法练习冲刺训练
-
华为OD算法/大厂面试高频题算法冲刺训练目前开始常态化报名!目前已服务100+同学成功上岸!
-
课程讲师为全网50w+粉丝编程博主@吴师兄学算法 以及小红书头部编程博主@闭着眼睛学数理化
-
每期人数维持在20人内,保证能够最大限度地满足到每一个同学的需求,达到和1v1同样的学习效果!
-
60+天陪伴式学习,40+直播课时,300+动画图解视频,300+LeetCode经典题,200+华为OD真题/大厂真题,还有简历修改、模拟面试、专属HR对接将为你解锁
-
可上全网独家的欧弟OJ系统练习华子OD、大厂真题
-
可查看链接 大厂真题汇总 & OD真题汇总(持续更新)
-
绿色聊天软件戳
od1336
了解更多