左神基础班01、计算数组的小和

题目描述:

数组小和的定义如下:

     例如,数组s = [1, 3, 5, 2, 4, 6],在s[0]的左边小于或等于s[0]的数的和为0;在s[1]的左边小于或等于s[1]的数的和为1;在s[2]的左边小于或等于s[2]的数的和为1+3=4;在s[3]的左边小于或等于s[3]的数的和为1;在s[4]的左边小于或等于s[4]的数的和为1+3+2=6;在s[5]的左边小于或等于s[5]的数的和1+3+5+2+4=15。所以s的小和为0+1+4+1+6+15=27。

     给定一个数组s,实现函数返回s的小和。要求时间复杂度为O(nlogn),空间复杂度为O(n)。

输入描述:

第一行有一个整数N。表示数组长度
接下来一行N个整数表示数组内的数

输出描述:

一个整数表示答案

示例1:

输入
6
1 3 5 2 4 6
输出
27

备注:
1⩽ N ⩽10^5
-100⩽ arr[i] ⩽ 100

原题链接:计算数组的小和

解题思路:

1、归并排序 + 递归:

      可以利用归并排序的思想,基本过程和归并排序一样,只不过在合并两个子数组时计算小和即可。设两个排好序的子数组分别为arr[l,mid]和arr[mid + 1,l],指针p1和p2分别指向左右子数组中的元素。具体的计算小和过程如下:

(1)当arr[p1] <= arr[p2]时,则计算小和,计算方式为res += (r - p2 + 1) * arr[p1],并将arr[p1]赋值给辅助数组help: help[i++] = arr[p1]

(2)当arr[p1] > arr[p2]时,则不用计算小和,直接将arr[p2]赋值给辅助数组help: help[i++] = arr[p2]

递归终止条件:l == r,返回0。

注:(1)当左边子数组中的元素等于右子数组的元素时,也要计算小和,因为这是题目的要求;(2)返回时最好使用long类型,防止数据溢出。

时间复杂度和空间复杂度: 时间复杂度为O(NlogN),空间复杂度为O(N)

实现代码:

import java.util.*;
public class Main{
	//解法1: 归并排序 + 递归
    public static long smallSumMerge(int[] arr, int l, int r){
        if(l == r || arr.length < 2)
            return 0;
        int mid = l + ((r - l) >> 1);//注意括号
        return smallSumMerge(arr, l, mid)
                + smallSumMerge(arr, mid + 1, r) + merge(arr, l, mid, r);
    }

    public static long merge(int[] arr, int l, int mid, int r){
        int[] help = new int[r - l + 1];
        long res = 0;
        int p1 = l, p2 = mid + 1;
        int i = 0;
        while(p1 <= mid && p2 <= r){
            //左边的数等于右边的数也算是一个小和
            res += arr[p1] <= arr[p2] ? arr[p1] * (r - p2 + 1) : 0;
            help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
        }
        while(p1 <= mid){
            help[i++] = arr[p1++];
        }
        while(p2 <= r){
            help[i++] = arr[p2++];
        }
        for(i = 0; i < help.length; ++i)
            arr[l + i] = help[i];
        return res;
    }

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        for(int i = 0; i < n; ++i)
            arr[i] = sc.nextInt();
        System.out.println(smallSumMerge(arr, 0, n - 1));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值