package com.example.demo.algorithm.D003;
/**
* @Description :
* 小和问题:理解归并排序的精髓
*
* 在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和,求数组小和。
* 例子:[1,3,4,2,5]
* 1左边比1小的数:没有
* 3左边比3小的数:1
* 4左边比4小的数:1、3
* 2左边比2小的数:1
* 5左边比5小的数:1、3、4、2
* 所以数组的小和为:1 + 1 + 3 + 1 + 1 + 3 + 4 + 2 = 16
*
* 流程:
* 右组的某个数比左组的大的有几个 == 左组的某个数在右组中有几个比它大,即右组的数就
* 1、左组去marge,返回小和
* 2、右组去marge,返回小和
* 3、左组和右组marge,返回小和
* [1,3,4,2,5]
* 0,1,2,3,4
* 134为左组 25为右组
* p1在0位置 p2在3位置
* 1 < 2 产生小和 从4的下标开始算有2个数大于1 所以小和为2个1 拷贝p1 p1++
* help[1]
*
* p1在1位置 p2在3位置
* 3 >(=) 2 不产生小和 拷贝p2 p2++
* help[1,2]
*
* p1在1位置 p2在4位置
* 3 < 5 产生小和 从5的下标开始算有1个数大于3 所以小和为1个3 拷贝p1 p1++
* help[1,2,3]
*
* p1在2位置 p2在4位置
* 4 < 5 产生小和 从5的下标开始算有1个数大于4 所以小和为1个4 拷贝p1 p1++
* help[1,2,3,4]
*
* 注:
* 左组数小于右组的数,产生小和为右组下标开始数有n个数比左组大,即n*左组数为小和,拷贝左组的数到help数组,左组下标++
* 左组数大于等于右组的数,不产生小和,拷贝右组的数到help数组,右组下标++
*
* 4、整体返回小和
*
* @Author : Darren
* @Date : 2021 年 02 月 21 日 18:35:44
* @since : 1.0
*/
public class J002_SmallSum {
public static void main(String[] args) {
int testTime = 500000;
int maxSize = 100;
int maxValue = 100;
boolean succeed = true;
for (int i = 0; i < testTime; i++) {
int[] arr1 = generateRandomArray(maxSize, maxValue);
int[] arr2 = copyArray(arr1);
if (smallSum(arr1) != smallSum2(arr2)) {
succeed = false;
printArray(arr1);
printArray(arr2);
break;
}
}
System.out.println(succeed ? "Nice!" : "Fucking fucked!");
}
public static int smallSum(int[] arr){
if (arr == null || arr.length < 2){
return 0;
}
return process(arr, 0, arr.length - 1);
}
public static int smallSum2(int[] arr){
if (arr == null || arr.length < 2){
return 0;
}
int res = 0;
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < i; j++) {
res += arr[j] < arr[i] ? arr[j] : 0;
}
}
return res;
}
/**
* arr[l..r]既要排好序,也要求小和返回
* 所有merge时,产生的小和累加
* 左排序 merge
* 右排序 merge
* @param arr
* @param l
* @param r
* @return
*/
private static int process(int[] arr, int l, int r) {
//只有一个数,不产生小和
if (l == r){
return 0;
}
int mid = l + ((r - l) >> 1);
//l …… mid的小和 + mid+1……r的小和 + l……r的小和
return process(arr, l, mid)
+ process(arr, mid + 1, r)
+ marge(arr, l, mid, r);
}
private static int marge(int[] arr, int l, int mid, int r) {
int[] help = new int[r - l + 1];
int i = 0;
int p1 = l;
int p2 = mid + 1;
int res = 0;
while (p1 <= mid && p2 <= r){
// 左组数小于右组的数,产生小和为右组下标开始数有n个数比左组大(因为有序),即n*左组数为小和,拷贝左组的数到help数组,左组下标++
// 左组数大于等于右组的数,不产生小和,拷贝右组的数到help数组,右组下标++
res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 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 (int i1 = 0; i1 < help.length; i1++) {
arr[l + i1] = help[i1];
}
return res;
}
/**
* 生成一个随机数组
* @param maxSize
* @param maxValue
* @return
*/
public static int[] generateRandomArray(int maxSize, int maxValue){
//Math.random() [0,1)
//Math.random() * N [0,N)
//(int)(Math.random() * N) [0,N-1]
int[] arrays = new int[(int) ((maxSize+1) * Math.random())];
for (int i = 0; i < arrays.length; i++) {
arrays[i] = (int) ((maxValue+1) * Math.random()) - (int)(maxValue * Math.random());
}
return arrays;
}
/**
* 打印数组
* @param arrays
*/
public static void printArray(int[] arrays){
if (arrays == null){
return;
}
for (int i = 0; i < arrays.length; i++) {
System.out.print(arrays[i] + " ");
}
System.out.println();
}
/**
* 复制数组
*/
public static int[] copyArray(int[] arr){
if (arr == null){
return null;
}
int[] res = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
res[i] = arr[i];
}
return res;
}
/**
* 判断两个数组是否相等
* @param arr1
* @param arr2
* @return
*/
public static boolean isArrayEqual(int[] arr1, int[] arr2){
if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)){
return false;
}
if (arr1 == null && arr2 == null){
return true;
}
if (arr1.length != arr2.length){
return false;
}
for (int i = 0; i < arr1.length; i++) {
if (arr1[i] != arr2[i]){
return false;
}
}
return true;
}
}
【知识积累】在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和,求数组小和
おすすめ
転載: blog.csdn.net/axin1240101543/article/details/114089879
ランキング