算法训练营day2:数组

算法训练营day2:数组

主要分为两种思路,滑动窗口或者二分+前缀和
思路一:滑动窗口,时间复杂度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;
}

思路:这道题用暴力求解会超时,需要分析丑数的数学特点。
分析可知若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];
    }
}

也可以通过打表实现。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值