算法训练营day2:数组
- 题目1:力扣209 长度最长的子数组
主要分为两种思路,滑动窗口或者二分+前缀和
思路一:滑动窗口,时间复杂度o(n)
大致思路以及讲解过程可以参考卡哥的视频
代码实现(C):
int minSubArrayLen(int target, int* nums, int numsSize) {
int left = 0;
int right;
int sum = 0, len = 0;
int count = numsSize;
for (right = 0; right < numsSize; right++) {
sum += nums[right];
len++;
while (sum >= target) {
if (count > len) {
count = len;
}
sum -= nums[left];
left++;
len--;
}
}
if (count == numsSize && len != numsSize - 1) {
count = 0;
}
return count;
}
这里主要注意结束条件len!=numsSize-1,对于数组所有元素之和刚好等于target的情况,比如[1,2,3,4,5],target=15,最后出循环count == numSize 且 len == numSize-1,所以单独的count==numSzie不能作为查找子数组失败的条件。
思路二:增加前缀和数组进行二分查找来寻找目标子数组,时间复杂度o(nlogn)。
首先分析该数组特点:
1. 对于非负整数数组,当找到某一子数组满足条件时,含有该子数组的其他子数组必定也满足条件。
2. 对于非负整数数组,其前缀和满足单调递增的特点,可通过二分查找(这里的二分查找是查找答案),并且通过前缀和数组元素作差可以求得任意区间内子数组之和。
代码实现(C++):
class Solution {
public:
// 前缀和 + 二分
static const int N = 1e5 + 10;
int preSum[N];
bool check(int len, int n, int target) {
for (int i = 0; i < n - len + 1; i++) {
if (i == 0) {
if (preSum[i + len - 1] >= target) return true;
}
else {
if (preSum[i + len - 1] - preSum[i - 1] >= target) return true;
}
}
return false;
}
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
preSum[0] = nums[0];
for (int i = 1; i < n; i++) {
preSum[i] = preSum[i - 1] + nums[i];
}
if (preSum[n - 1] < target) return 0;
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (check(mid, n, target)) r = mid;
else l = mid + 1;
}
return l;
}
};
这道题需要注意时间,一般暴力求解会超时。这道题需要注意C的标准输入输出scanf和printf会比C++的输入输出cin和cout快。
我先试了一下暴力求法,没想到也过了,但是时间有点极限,800ms。
后来我测试了一下,原来是我部分输入输出用的C语言的,勉强没有超时,如果纯用C++的输入输出,耗时会高达2000ms,超出时间限制。
//800ms
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int arr[100000] = {};
int n, a;
int sum = 0;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
int x, y;
while (~scanf("%d %d", &x, &y)) {
sum = 0;
for (int i = x; i <= y; i++) {
sum += arr[i];
}
printf("%d\n", sum);
}
return 0;
}
但是如果用前缀和求解,只需要500ms左右:
//587ms
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int n, a;
int sum = 0;
cin >> n;
int arr[n];
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
int preSum[n];
//前缀和
preSum[0] = arr[0];
for (int i = 1; i < n; i++) {
preSum[i] = preSum[i - 1] + arr[i];
}
int x, y;
while (cin >> x >> y) {
cout << preSum[y] - preSum[x - 1] << endl;
}
return 0;
}
将上述代码中的输入输出改为C的输入输出,只需要34ms:
//34ms
#include <cstdio>
#include <iostream>
using namespace std;
int main() {
int n, a;
int sum = 0;
scanf("%d", &n);
int arr[n];
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
int preSum[n];
//前缀和
preSum[0] = arr[0];
for (int i = 1; i < n; i++) {
preSum[i] = preSum[i - 1] + arr[i];
}
int x, y;
while (~scanf("%d %d", &x, &y)) {
printf("%d\n", preSum[y] - preSum[x - 1]);
}
return 0;
}
- 题目3:力扣264 丑数II
思路:这道题用暴力求解会超时,需要分析丑数的数学特点。
分析可知若N是丑数,则2N,3N,5N也是丑数。
首先置arr[1]=1,即第一个丑数是1,则2的丑数序列为[2,4,6,8,…],3的丑数序列为[3,6,9,12,…],5的丑数序列为[5,10,15,20,…],通过三路归并填入数组中,如下图为求前7个丑数的过程。

代码实现(C):
int min(int a, int b) {
int max;
if (a > b) {
max = b;
} else {
max = a;
}
return max;
}
int nthUglyNumber(int n) {
if (n <= 1) {
return 1;
} else {
int arr[n + 1] = {};
arr[1] = 1;
int p2 = 1, p3 = 1, p5 = 1;
for (int i = 2; i < n + 1; i++) {
int a = arr[p2] * 2;
int b = arr[p3] * 3;
int c = arr[p5] * 5;
int mind = min(a, min(b, c));
if (mind == a)
p2++;
if (mind == b)
p3++;
if (mind == c)
p5++;
arr[i] = mind;
printf("%d ", arr[i]);
}
return arr[n];
}
}
也可以通过打表实现。
750

被折叠的 条评论
为什么被折叠?



