当前位置 博文首页 > Inmaturity_7的博客:算法练习贴--22--子数组和排序后的区间和(J

    Inmaturity_7的博客:算法练习贴--22--子数组和排序后的区间和(J

    作者:[db:作者] 时间:2021-08-01 11:46

    子数组和排序后的区间和

    给你一个数组 nums ,它包含 n 个正整数。你需要计算所有非空连续子数组的和,并将它们按升序排序,得到一个新的包含 n * (n + 1) / 2 个数字的数组。

    请你返回在新数组中下标为 left 到 right (下标从 1 开始)的所有数字和(包括左右端点)。由于答案可能很大,请你将它对 10^9 + 7 取模后返回。

    (来源:力扣(LeetCode))

    示例 1:
    
    输入:nums = [1,2,3,4], n = 4, left = 1, right = 5
    输出:13 
    解释:所有的子数组和为 1, 3, 6, 10, 2, 5, 9, 3, 7, 4 。将它们升序排序后,我们得到新的数组 [1, 2, 3, 3, 4, 5, 6, 7, 9, 10] 。下标从 le = 1 到 ri = 5 的和为 1 + 2 + 3 + 3 + 4 = 13 。
    
    示例 2:
    
    输入:nums = [1,2,3,4], n = 4, left = 3, right = 4
    输出:6
    解释:给定数组与示例 1 一样,所以新数组为 [1, 2, 3, 3, 4, 5, 6, 7, 9, 10] 。下标从 le = 3 到 ri = 4 的和为 3 + 3 = 6 。
    
    示例 3:
    
    输入:nums = [1,2,3,4], n = 4, left = 1, right = 10
    输出:50
    
    提示:
        1 <= nums.length <= 10^3
        nums.length == n
        1 <= nums[i] <= 100
        1 <= left <= right <= n * (n + 1) / 2
    

    解决方法一(排序):

    class Solution {
        public int rangeSum(int[] nums, int n, int left, int right) {
            final int MODULO = 1000000007;
            int sumsLength = n * (n + 1) / 2;
            int[] sums = new int[sumsLength];
            int index = 0;
            for (int i = 0; i < n; i++) {
                int sum = 0;
                for (int j = i; j < n; j++) {
                    sum += nums[j];
                    sums[index++] = sum;
                }
            }
            Arrays.sort(sums);
            int ans = 0;
            for (int i = left - 1; i < right; i++) {
                ans = (ans + sums[i]) % MODULO;
            }
            return ans;
        }
    }
    

    解决方法二(双指针+二分查找)

    class Solution {
        static final int MODULO = 1000000007;
    
        public int rangeSum(int[] nums, int n, int left, int right) {
            int[] prefixSums = new int[n + 1];
            prefixSums[0] = 0;
            for (int i = 0; i < n; i++) {
                prefixSums[i + 1] = prefixSums[i] + nums[i];
            }
            int[] prefixPrefixSums = new int[n + 1];
            prefixPrefixSums[0] = 0;
            for (int i = 0; i < n; i++) {
                prefixPrefixSums[i + 1] = prefixPrefixSums[i] + prefixSums[i + 1];
            }
            return (getSum(prefixSums, prefixPrefixSums, n, right) - getSum(prefixSums, prefixPrefixSums, n, left - 1)) % MODULO;
        }
    
        public int getSum(int[] prefixSums, int[] prefixPrefixSums, int n, int k) {
            int num = getKth(prefixSums, n, k);
            int sum = 0;
            int count = 0;
            for (int i = 0, j = 1; i < n; i++) {
                while (j <= n && prefixSums[j] - prefixSums[i] < num) {
                    j++;
                }
                j--;
                sum = (sum + prefixPrefixSums[j] - prefixPrefixSums[i] - prefixSums[i] * (j - i)) % MODULO;
                count += j - i;
            }
            sum = (sum + num * (k - count)) % MODULO;
            return sum;
        }
    
        public int getKth(int[] prefixSums, int n, int k) {
            int low = 0, high = prefixSums[n];
            while (low < high) {
                int mid = (high - low) / 2 + low;
                int count = getCount(prefixSums, n, mid);
                if (count < k) {
                    low = mid + 1;
                } else {
                    high = mid;
                }
            }
            return low;
        }
    
        public int getCount(int[] prefixSums, int n, int x) {
            int count = 0;
            for (int i = 0, j = 1; i < n; i++) {
                while (j <= n && prefixSums[j] - prefixSums[i] <= x) {
                    j++;
                }
                j--;
                count += j - i;
            }
            return count;
        }
    }
    
    
    
    cs