LeetCode 327 区间和的个数 C语言
给定一个整数数组 nums,返回区间和在 [lower, upper] 之间的个数,包含 lower 和 upper。
区间和 S(i, j) 表示在 nums 中,位置从 i 到 j 的元素之和,包含 i 和 j (i ≤ j)。
说明:
最直观的算法复杂度是 O(n2) ,请在此基础上优化你的算法。
示例:
输入: nums = [-2,5,-1], lower = -2, upper = 2,
输出: 3
解释: 3个区间分别是: [0,0], [2,2], [0,2],它们表示的和分别为: -2, -1, 2。
思路:
以数组{-2147483647,0,-2147483647,2147483647}中,当判断上下限的情况时会报错。
可知INT_MAX = 2147483648(0x 7FFF FFFF);INT_MIN = -2147483647(0x 8000 0000);
INT_MAX + INT_MAX = -2(0x FFFF FFFE);INT_MIN + INT_MIN = 0 (0x 1000 0000)
首先了解什么是前缀和:假设 Sum[i] 是数组 nums[1]、nums[2],…,nums[n] 的前缀和,则:
preSum[i] = nums[1]+nums[2] +…+nums[i];数组 nums 的子数组nums[i,…,j] 可以表示为 preSum[j]-preSum[i-1]。
从题出发要求的是当i < j 时 lower <= Sum[j] - Sum[i] <= upper,从中可以发现求逆序对时,是这题的Sum[j] - Sum[i] < 0 的个数(此时归并排序的传参数组为前缀和Sum);以下图为例(来自下面链接中的作者)https://leetcode-cn.com/problems/count-of-range-sum/solution/327qu-jian-he-de-ge-shu-ti-jie-zong-he-by-xu-yuan-/
设下限为0,上限为4
首先Left指针指向蓝色部分最左端,Lower指针和Upper指针指向黄色部分最左端。
当Sum[Lower] - Sum[Left] < 0就继续移动Lower;当Sum[Upper] - Sum[Left] <= 4就继续移动Upper得到(这里就是找到上限和下限的位置,左边没有=是因为判断比下限小就移动指针,直到找到>=0,右边则是找到满足比上限小的就移动指针,直到找到>4,因此区间还是在[ 0 , 4 ]中的)
此时Upper - Lower就是Left(-1)所对应的个数,这里等于0,因为Upper和Lower之后的值更大,更不可能满足要求。接着向左移动Left,但是Upper和Lower并不需要往后移动。
因此Left(0)对应个数为0,继续移动Left,并相应地移动Upper和Lower得到:
可以得到Left(7)对应个数为2,最后移动Left,Upper和Lower得到:
可以得到Left(9)对应个数为0。
因此最后答案为2,并且我们通过线性的实现就完成了求解过程,因为Left,Upper,Lower都只向右扫描了一遍。
calloc 和 malloc的区别:calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不做初始化,分配到的空间中的数据是随机数据。void* calloc(unsigned int num,unsigned int size);<小插曲 突然看到了>
void MergeSort(long *nums,int start,int mid,int end,int lower,int upper,int* count){
int i = start;
int j = mid+1;
int k = 0;
int len = end-start+1;
long *temp = (long *)malloc(sizeof(long)*len);
while(i <= mid && j <= end){
if(nums[i]<nums[j]){
temp[k++] = nums[i++];
}
else{
temp[k++] = nums[j++];
}
}
if(i == mid+1){
while(j<=end){
temp[k++] = nums[j++];
}
}
if(j == end+1){
while(i <= mid){
temp[k++] = nums[i++];
}
}
i = start;
int Upper = mid+1;
int Lower = mid+1;
while(i <= mid){
while(Lower <= end && nums[Lower]-nums[i] < lower)
Lower++;
while(Upper <= end && nums[Upper]-nums[i] <= upper)
Upper++;
(*count) += Upper - Lower;
i++;
}
for(i=start,j=0;j<len;i++,j++){
nums[i] = temp[j];
}
free(temp);
temp = NULL;
}
void Merge(long *nums,int start,int end,int lower,int upper,int *count){
if(start<end){
int mid = (start + end)/2;
Merge(nums,start,mid,lower,upper,count);
Merge(nums,mid+1,end,lower,upper,count);
MergeSort(nums,start,mid,end,lower,upper,count);
}
}
int countRangeSum(int* nums, int numsSize, int lower, int upper){
long * Sum =(long *)malloc(sizeof(long)*(numsSize+1));//前缀和
memset(Sum,0,sizeof(long)*(numsSize+1));
Sum[0] = 0;
for(int i=1;i<numsSize+1;i++){
Sum[i] = Sum[i-1] + nums[i-1];
}
int count = 0;
Merge(Sum,0,numsSize,lower,upper,&count);
return count;
}
//VSCode实现区间和问题
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void MergeSort(long long *nums,int start,int mid,int end,int lower,int upper,int* count){
int i = start;
int j = mid+1;
int k = 0;
int len = end-start+1;
long long *temp = (long long *)malloc(sizeof(long long)*len);
while(i <= mid && j <= end){
if(nums[i] < nums[j]){
temp[k++] = nums[i++];
}
else{
temp[k++] = nums[j++];
}
}
if(i == mid+1){
while(j <= end){
temp[k++] = nums[j++];
}
}
if(j == end+1){
while(i <= mid){
temp[k++] = nums[i++];
}
}
i = start;
int Upper = mid + 1;
int Lower = mid + 1;
while(i <= mid){
while(Lower <= end && nums[Lower]-nums[i] < lower)
Lower++;
while(Upper <= end && nums[Upper]-nums[i] <= upper)
Upper++;
(*count) += Upper - Lower;
i++;
}
for(i=start,j=0;j<len;i++,j++){
nums[i] = temp[j];
}
free(temp);
temp = NULL;
}
void Merge(long long *nums,int start,int end,int lower,int upper,int *count){
if(start < end){
int mid = (start + end)/2;
Merge(nums,start,mid,lower,upper,count);
Merge(nums,mid+1,end,lower,upper,count);
MergeSort(nums,start,mid,end,lower,upper,count);
}
}
void print_list(long long *nums,int len){
for(int i=0;i<len;i++){
printf("%lld ",nums[i]);
}
printf("\n");
}
void main(){
//int nums[] = {-2, 5, -1};
int nums[] = {-2147483647,0,-2147483647,2147483647};
int lower = -564;
int upper = 3864;
//int a2= -2147483647;
//long long b2=a2;
//printf("a2 = %d\n",a2);
//printf("b2 = %lld\n",b2);
//printf("%lld",a2+b2);
//Vscode 中 sizeof(long) == sizeof(int);
//INT_MAX + 1 = INT_MIN; //#define INT_MAX 2147483647
//INT_MAX + INT_MAX = -2;//#define INT_MIN -2147483648
//INT_MIN + INT_MIN = 0;
int len = sizeof(nums)/sizeof(int);
int count = 0;
//print_list(nums,len);
long long *Sum = (long long *)malloc(sizeof(long long)*(len+1));
memset(Sum,0,sizeof(long long)*(len+1));
Sum[0]=0;
for(int i=1;i<len+1;i++){
Sum[i] = Sum[i-1] + nums[i-1];
}
printf("打印排序前前缀和\n");
print_list(Sum,len+1);
Merge(Sum,0,len,lower,upper,&count);
printf("打印排序后前缀和\n");
print_list(Sum,len+1);
printf("%d\n",count);
}