LeetCode 327 区间和的个数 C语言

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
区间和1
   首先Left指针指向蓝色部分最左端,Lower指针和Upper指针指向黄色部分最左端。
区间和2
   当Sum[Lower] - Sum[Left] < 0就继续移动Lower;当Sum[Upper] - Sum[Left] <= 4就继续移动Upper得到(这里就是找到上限和下限的位置,左边没有=是因为判断比下限小就移动指针,直到找到>=0,右边则是找到满足比上限小的就移动指针,直到找到>4,因此区间还是在[ 0 , 4 ]中的)
区间和3
   此时Upper - Lower就是Left(-1)所对应的个数,这里等于0,因为Upper和Lower之后的值更大,更不可能满足要求。接着向左移动Left,但是Upper和Lower并不需要往后移动。
区间和4
   因此Left(0)对应个数为0,继续移动Left,并相应地移动Upper和Lower得到:
   
区间和5
   可以得到Left(7)对应个数为2,最后移动Left,Upper和Lower得到:
区间和6
   可以得到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);	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值