• [LeetCode No.327] 区间和个数


    前序(废话)
    因为对考研分数没信心,博主毕业前也尝试了找工作。有尝试就知道自己不足,发现自己还是太弱了,技术栈不行,算法也不行。
    虽然最后考研没录上一志愿,但是可以给自己三年时间来慢慢打磨技术、沉淀一下。痛定思过,决定有时间就一天一道LeetCode。btw:希望三年能坚持下来

    题目

    给定一个整数数组 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。

    题解

    由于这是第一道自己刷的题,一随机还随机了一道困难题,看了两天终于看懂了
    这道题有好几种解法,这里主要介绍一个博主自己写的暴力法和博客园的标准答案归并排序法

    思路一:暴力遍历

    暴力遍历很简单,就是使用两层for循环遍历所有可能的S(i,j)。注:不要忘记一个元素满足条件的情况
    当然这里有个小坑 sum累计和需要使用long,如若使用int,当两个极大的int相加会溢出

    代码

    class Solution {
    	    public static int countRangeSum(int[] nums, int lower, int upper) {
    			int length = nums.length;    //记录数组长度
    			int count = 0;				//统计符合要求区间个数
    			int j;						//S(i,j)中的j
    			int i;						//S(i,j)中的i
    			for(j=0;j<length;j++){          //j可以取到数组所有的元素  固遍历
    	                if(nums[j]>=lower&&nums[j]<=upper)		//在j遍历过程中,看是否有单个元素符合lower-upper要求
    	        	count++;
    	                long sum=nums[j];                //计算累计和
    				for(i=j-1;i>=0;i--){				//将i从j前一个元素遍历、计算累计和
    					sum += nums[i];				
    					if(sum>=lower&&sum<=upper)		//满足条件 count++
    						count++;
    					}        
    				
    				}
    			return count;
    	}
    }
    

    思路二:归并排序法

    归并排序法是博客园官方解答中的解法一,u1s1这个解法粗看上去,还是有难度的,我看了几遍答案,第二天看了其他人的题解解释才弄懂

    首先我们先看一下官方的题解和代码

    其实官方题解给的导入例子:给两个升序排列数组,找出符合的下标对。还是很容易理解的
    但是突然来了句原问题就迎刃而解了,属实给我整懵了。找了好久网上的解释,参考了一下大佬的解释(https://leetcode-cn.com/problems/count-of-range-sum/solution/shuo-ming-yi-xia-guan-fang-gui-bing-pai-xu-by-sing/)才终于搞懂官方的意思

    对于给定的输入:nums = [2,-1,4,3], lower = 1, upper = 4
    我们可以先构造一个前缀和数组sums=[0,2,1,5,8],sums[i]就是nums数组索引[0,i-1]的和,如sums[1]=2=nums[0],sums[2]=1=nums[0]+nums[1]
    那么如果想找nums的区间和,我们可以通过前缀和数组计算得出。如S(1,2)=4=sums[3]-sums[1] 找出满足要求的区间和 即可转换为 通过前缀和数组的计算获取
    对于前缀和数组 我们就可以结合官方给出的导入例子来理解
    可以将前缀和数组一分为二,一分为二.....。分到最小的时候(就每个组只有一个元素 从左来看即 左组为[0],右组为[2]。从右看,左组[4],右组为[3] )
    我们从左来理解,两组只有一个元素,显然两组因为只有一个元素,所以两组都是升序数组。
    那么我们可以使用导例,将[0]看成n1,[2]看成n2。那么则可统计符合要求下标对数量

    然后我们把n1,n2进行合并,排序。 右组也是一样n1,n2合并排序,到最后从左右合并排序完,就得到了n1 = [0,1,2] n2=[5,8] 又可以求横跨两组符合要求的下标对数量
    就求出了所有符合要求下标对数量

    代码

    class Solution {
        public int countRangeSum(int[] nums, int lower, int upper) {
            long s = 0;
            long[] sum = new long[nums.length + 1];
            for (int i = 0; i < nums.length; ++i) {
                s += nums[i];
                sum[i + 1] = s;
            }
            return countRangeSumRecursive(sum, lower, upper, 0, sum.length - 1);
        }
    
        public int countRangeSumRecursive(long[] sum, int lower, int upper, int left, int right) {
            if (left == right) {
                return 0;
            } else {
                int mid = (left + right) / 2;
                int n1 = countRangeSumRecursive(sum, lower, upper, left, mid);
                int n2 = countRangeSumRecursive(sum, lower, upper, mid + 1, right);
                int ret = n1 + n2;
    
                // 首先统计下标对的数量
                int i = left;
                int l = mid + 1;
                int r = mid + 1;
                while (i <= mid) {
                    while (l <= right && sum[l] - sum[i] < lower) {
                        l++;
                    }
                    while (r <= right && sum[r] - sum[i] <= upper) {
                        r++;
                    }
                    ret += r - l;
                    i++;
                }
    
                // 随后合并两个排序数组
                int[] sorted = new int[right - left + 1];
                int p1 = left, p2 = mid + 1;    //p1用作n1    p2用于n2
                int p = 0;                 //用作sorted右索引定位
                while (p1 <= mid || p2 <= right) {
                    if (p1 > mid) {
                        sorted[p++] = (int) sum[p2++];
                    } else if (p2 > right) {
                        sorted[p++] = (int) sum[p1++];
                    } else {
                        if (sum[p1] < sum[p2]) {
                            sorted[p++] = (int) sum[p1++];
                        } else {
                            sorted[p++] = (int) sum[p2++];
                        }
                    }
                }
                for (int j = 0; j < sorted.length; j++) {
                    sum[left + j] = sorted[j];
                }
                return ret;
            }
        }
    }
    
    
  • 相关阅读:
    div在父集高度未知的情况下垂直居中的方法
    固比固布局 圣杯布局 css实现传统手机app布局
    img标签的onerror事件
    vue中的swiper element ui
    前后端分离跨域 关于前后端分离开发环境下的跨域访问问题(angular proxy=>nginx )
    自己开发的网页在跳转至微信公众号文章后,点击微信的返回,无法返回原网页
    关于audio元素在实际项目中遇到的问题总结
    移动端HTML5<video>视频播放优化实践
    数据类型转换
    穿越宇宙的邀请函——镜像图片技巧
  • 原文地址:https://www.cnblogs.com/Mr-BING/p/13945533.html
Copyright © 2020-2023  润新知