赞
踩
题目描述:
数组小和的定义如下:
例如,数组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)); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。