什么是"小和":
在一个序列中,按照从左到右的方向,求每个位置上左边比它小的数的和,即“小和”。
如:1、2这个序列整体小和为1
2,4,5,3,1这个序列:
2的小和为0
4的小和为2
5的小和为6
3的小和为2
1的小和为0
最后将这些数相加,得到整个序列的小和为 10
第一种解法:普通for循环,时间复杂度O(n^2)
public static int simpleSmallAnd(int[] arr){
int sum = 0;
for(int i = arr.length-1;i > 0;i--){
for(int j = i-1;j >= 0;j--){
if(arr[i] > arr[j]){
sum += arr[j];
}
}
}
return sum;
}
第二种解法:使用归并排序,时间复杂度O(N*logN)
master公式:
参数解读:
N:问题规模
b:问题规模分成多少个子规模
a:子规模调用次数
d除去子规模其它代码的执行复杂度。
结论:时 时间复杂度:
:时 时间复杂度:
: 时间复杂度:
在下列代码中:为你规模为N,b为2(分为两半操作,所以子规模为2),a也为2,d为1,
和比较,,符合: 时间复杂度:
这时,所以利用递归求"小和"问题的时间复杂度为:
package com.kali.logN;
import java.util.Arrays;
/**
* 小和问题:
* 1 3 4 2 5 7
* 1 左边比它小的 没有 小和0
* 3 1
* 4 4
* 2 1
* 5 10
* 7 15
* 整个数组的小和是 1 + 4 + 1 + 10 + 15
* 思路:
* 1.循环遍历i++的范围中的小和
* 2.从第i个位置起看后面有多少个数比它大那么这个位置就产生多少次和
*/
public class SmallAnd {
public static void main(String[] args) {
int[] arr = {1,3,4,2,5};
/*int i = simpleSmallAnd(arr);
System.out.println(i);*/
int execute = execute(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
System.out.println(execute);
}
public static int execute(int[] arr,int L,int R){
if(L == R){
return 0;
}
int mid = L + ((R - L) >> 1);
return execute(arr,L,mid) +
execute(arr,mid+1,R) +
merge(arr,L,mid,R);
}
public static int merge(int[] arr,int L,int mid,int R){
int and = 0;
int[] temp = new int[R-L+1];
int left = L;
int right = mid+1;
int k = 0;
while(left <= mid && right <= R){
//在merge两个有序序列时,arr[left] < arr[right],产生的小和应该要乘以右边的序列size,其他情况返回0
and += arr[left] < arr[right] ? arr[left] * (R-right+1) : 0;
//如果两边相等得先返回右边的,防止左边指针移动导致取不到小于右边时的小和
temp[k++] = arr[left] < arr[right] ? arr[left++] : arr[right++];
}
while(left <= mid){
temp[k++] = arr[left++];
}
while(right <= R){
temp[k++] = arr[right++];
}
for(int i = 0;i < temp.length;i++){
arr[L+i] = temp[i];
}
return and;
}
}