差分 前缀和
根据灵神题单,把前缀和和差分拿出来讲解,大家会了之后记得刷题,容易脑子会了,做不出来题目。
前缀和
前缀和背景
数组中频繁查询某段区间的和,如何优化? 303. 区域和检索 - 数组不可变
前缀和算法
初步想法
目的:求和 arr[left]+arr[left+1]+arr[left+2]+…+ arr[right]
观察下面的数组
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
arr[0] | arr[0]+arr[1] | arr[0]+arr[1]+arr[2] | arr[0]+arr[1]+arr[2]+arr[3] | arr[0]+arr[1]+arr[2]+arr[3]+arr[4] | arr[0]+arr[1]+arr[2]+arr[3]+arr[4]+arr[5] |
sum(left,right) = vct[right]-vct[left-1]
注意到,这里要求left >0,需要每次加一个判断,就不是很优雅
加上哨兵
原数组
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
arr[0] | arr[1] | arr[2] | arr[3] | arr[4] | arr[5] |
加上哨兵
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
0 | arr[0] | arr[1] | arr[2] | arr[3] | arr[4] | arr[5] |
前缀和数组
0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
0 | arr[0] | arr[0]+arr[1] | arr[0]+arr[1]+arr[2] | arr[0]+arr[1]+arr[2]+arr[3] | arr[0]+arr[1]+arr[2]+arr[3]+arr[4] | arr[0]+arr[1]+arr[2]+arr[3]+arr[4]+arr[5] |
sum(left,right) = vct[right+1]-vct[left] 就不要做判断了
例题
例题一
303. 区域和检索 - 数组不可变
解法:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct {int *nums;int numSize;
} NumArray;NumArray *numArrayCreate(int *nums, int numsSize) {NumArray *numArray = (NumArray *)malloc(sizeof(NumArray));numArray->nums = (int *)malloc(sizeof(int) * (numsSize + 1));numArray->nums[0] = 0;numArray->numSize = numsSize+1;memcpy(&numArray->nums[1], nums, numsSize * sizeof(int));for (int i = 1; i < numArray->numSize; i++) {numArray->nums[i] += numArray->nums[i - 1];}return numArray;
}int numArraySumRange(NumArray *obj, int left, int right) {return (obj->nums[right + 1] - obj->nums[left]);
}void numArrayFree(NumArray *obj) {free(obj->nums);obj->nums = NULL;free(obj);
}
例题二
53. 最大子数组和
超时解法
#include <stdio.h>
#include <stdlib.h>
#define max(a, b) ((a) > (b) ? (a) : (b))
int maxSubArray(int *nums, int numsSize) {int res = nums[0];for (int i = 1; i < numsSize; i++) {nums[i] += nums[i - 1];}for (int left = 0; left < numsSize ; left++) {for (int right = left; right < numsSize; right++) {if (left == 0) {res = max(res, nums[right]);} else {res = max(res, nums[right] - nums[left-1]);}}}return res;
}
复杂度太大了,一直在重复计算最小值
#include <stdio.h>
#include <stdlib.h>
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) > (b) ? (b) : (a))
int maxSubArray(int *nums, int numsSize) {int res = nums[0];int minPreSum = 0;int nowSum = 0;for (int i = 0; i < numsSize; i++) {nowSum += nums[i];res = max(res, nowSum - minPreSum);minPreSum = min(minPreSum, nowSum);}return res;
}
正确解法
这里还有一个动态规划的解法
class Solution {
public:int maxSubArray(vector<int> &nums) {int nowRes = nums.front();int targetRes = nowRes;for (int i = 1; i < nums.size(); i++) {nowRes = max(nowRes + nums[i], nums[i]);targetRes = max(targetRes, nowRes);//nowRes 表示以nums[i]结尾的,最大子数组和 新的这个最大子数组,要么是nums[i],要么加上之前的值}return targetRes;}
};
例题三
930. 和相同的二元子数组
解法
class Solution {public:int numSubarraysWithSum(vector<int> &nums, int goal) {unordered_map<int, int> mp;nums.insert(nums.begin(), 0);mp[0]=1;int res = 0;for (int i = 1; i < nums.size(); i++) {nums[i] += nums[i - 1];int targetNum = nums[i] - goal;res += mp[targetNum];mp[nums[i]]++;}return res;}
};
例题四 二维前缀和
二维前缀和解法
#include <stdio.h>
#include <stdlib.h>
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) > (b) ? (b) : (a))int **matrixBlockSum(int **mat, int matSize, int *matColSize, int k, int *returnSize,int **returnColumnSizes) {int m = matSize;int n = *matColSize;// Create prefix sum matrix with extra row and column of zerosint **prefixSum = (int **)malloc(sizeof(int *) * (m + 1));for (int i = 0; i <= m; i++) {prefixSum[i] = (int *)calloc(n + 1, sizeof(int));}// Calculate prefix sumsfor (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {prefixSum[i][j] = prefixSum[i - 1][j] + prefixSum[i][j - 1] - prefixSum[i - 1][j - 1] +mat[i - 1][j - 1];}}// Allocate result matrixint **result = (int **)malloc(sizeof(int *) * m);*returnColumnSizes = (int *)malloc(sizeof(int) * m);*returnSize = m;for (int i = 0; i < m; i++) {result[i] = (int *)malloc(sizeof(int) * n);(*returnColumnSizes)[i] = n;for (int j = 0; j < n; j++) {int r1 = max(0, i - k);int c1 = max(0, j - k);int r2 = min(m - 1, i + k);int c2 = min(n - 1, j + k);result[i][j] = prefixSum[r2 + 1][c2 + 1] - prefixSum[r2 + 1][c1] -prefixSum[r1][c2 + 1] + prefixSum[r1][c1];}}// Free prefix sum memoryfor (int i = 0; i <= m; i++) {free(prefixSum[i]);}free(prefixSum);return result;
}
差分
差分原理
原网址
例题一
拼车
#include <stdbool.h>bool carPooling(int **trips, int tripsSize, int *tripsColSize, int capacity) {int vct[1002] = {0};for (int i = 0; i < tripsSize; i++) {vct[trips[i][1]] += trips[i][0];vct[trips[i][2]] -= trips[i][0];}if (vct[0] > capacity) {return false;}for (int i = 1; i < 1002; i++) {vct[i] += vct[i - 1];if (vct[i] > capacity) {return false;}}return true;
}
例题二
2848. 与车相交的点
很简单,类似于合并区间,鉴于数据量比较小,就可以用int存
例题三
所有排列中的最大和
根据频率找访问量最大的地方,然后把他给最大的数字
typedef struct {int reqTime;int index;
} Node;int cmp(const void *nodeA, const void *nodeB) {Node *a = (Node *)nodeA;Node *b = (Node *)nodeB;return (a->reqTime > b->reqTime);
}
int cmpInt(const void *nodeA, const void *nodeB) {int a = *(int *)nodeA;int b = *(int *)nodeB;return a > b;
}
int maxSumRangeQuery(int *nums, int numsSize, int **requests, int requestsSize,int *requestsColSize) {Node *vct = (Node *)calloc(sizeof(Node), (numsSize + 2));for (int i = 0; i < requestsSize; i++) {vct[requests[i][0]].reqTime++;vct[requests[i][1] + 1].reqTime--;}vct[0].index = 0;for (int i = 1; i < numsSize; i++) {vct[i].reqTime += vct[i-1].reqTime;vct[i].index = i;}qsort(vct, numsSize, sizeof(Node), cmp);qsort(nums, numsSize, sizeof(int), cmpInt);long long res = 0;const long long YU_SHU = 1e9 + 7;for (int i = 0; i < numsSize; i++) {res = (res + (long long)vct[i].reqTime * nums[i]) % YU_SHU;}return res;
}