11.2:小和问题,暴力求解和归并思想求解
11.2:小和问题,暴力求解和归并思想求解
例:
[ 6 3 2 1 6 7]
每个数左边,比它小的数累加起来
6; 6的左边有多少个数比6小,0个;
3; 3的左边有多少个数比3小,0个;
2; 2的左边有多少个数比2小,0个;
1; 1的左边有多少个数比1小,0个;
6; 6的左边有多少个数比6小,3个:分别是 3 2 1,加起来和为6;
7; 7的左边有多少个数比7小,5个:分别是 6 3 2 1 6,加起来和为18;
0 0 0 0 6 18
所有的小和全部加起来 = 24
求每个数左边,比它小的数累加起来 =转换为= 求每个数右边有几个比它大的数
对于数组里的每一个数x,必然发生:
x与靠它最近的右组进行比较,求出有几个x比它大; merge后
x与靠它次近的更大右组进行比较,求出有几个x比它大;合并后
..........................................................................................................................
例: [ 6 3 2 1 6 7 100 3]
关注数字1这个数:
数组分左右组: [ 6 3 2 1 ][ 6 7 100 3]
在左组这个区域内,在递归和merge的过程中是不产生小和的,因为
我们给的策略是:左组数大,右组数小,不产生小和。
但是在遭遇自己右组的时候 [ 1 2 3 6 ][ 6 7 100 3]
看这个数字1:
策略:左组数小,右组数达,产生小和,产生几个小和?
在拷贝数字1的时候,产生[ 3 6 7 100],有4个数比1大,产生4个1的小和。
又假如[ 6 3 2 1 ][ 6 7 100 3] 还有更大的右组
[ 6 3 2 1 ][ 6 7 100 3] [144 122 3 0 59 1 ....]
排好序[ 1 2 3 3 6 6 7 100]
排好序 [0 1 3 59 122 144 ..... ]
我们在merge的时候:关注数字1
在拷贝1的时候,产生[ 3 59 122 144 .....],有多少个数比数字1大,
产生多少个数字1的小和。
这也正是利用的归并排序的特点
每一次比较行为都变成了结果并且再传递下去。
O(N^2)的做法:
1、循环到某个数,遍历它的左边,把它左边小于这个数的数全部加起来
2、从头到尾循环一遍
3、最后算总的和。
用归并的思想让时间复杂度变成O(N * logN)
利用的是mergeSort的实质:
1、通过递归,将数组不断划分,知道左右都为1个时
2、利用merge的过程,遵循:√左组数小,右组数大产生小和√
2.1、谁小拷贝谁;
2.2、相等先拷贝右组;
右组指针往后;
2.3、左组数大,右组数小(题目要求:找这个数的左边有多少个数比它小,也就是右边的数要大,
现在右边的数小,肯定不产生小和),
拷贝右组数时(谁小拷贝谁),不产生小和;
右组指针往后;
2.4、左组数小,右组数大,拷贝左组数时(谁小拷贝谁),看一下目前右组有多少个数比它大,产生多少个小和;
左组指针往后;
暴力的求解:
1 public static int comparator(int[] arr) {
2 if (arr == null || arr.length < 2) {
3 return 0;
4 }
5 int res = 0;
//傻暴力
6 for (int i = 1; i < arr.length; i++) {
7 for (int j = 0; j < i; j++) {
//右侧有的就累加arr[j],别写成arr[i];否则是0
8 res += arr[j] < arr[i] ? arr[j] : 0;
9 }
10 }
11 return res;
12 }
1 public static int smallSum(int[] arr) {
2 if (arr == null || arr.length < 2) {
3 return 0;
4 }
5 return process(arr, 0, arr.length - 1);
6 }
7
8 // arr[L..R]既要排好序,也要求小和返回
9 // 所有merge时,产生的小和,累加
10 // 左 排序 merge
11 // 右 排序 merge
12 // merge
13 public static int process(int[] arr, int l, int r) {
14 if (l == r) {
15 return 0;
16 }
17 // l < r
18 int mid = l + ((r - l) >> 1);
19 return
20 process(arr, l, mid)
21 +
22 process(arr, mid + 1, r)
23 +
24 merge(arr, l, mid, r);
25 }
26
27 public static int merge(int[] arr, int L, int m, int r) {
28 int[] help = new int[r - L + 1];
29 int i = 0;
30 int p1 = L;
31 int p2 = m + 1;
32 int res = 0;
33 while (p1 <= m && p2 <= r) {
34 res += arr[p1] < arr[p2] ? (r - p2 + 1) * arr[p1] : 0;
35 help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
36 }
37 while (p1 <= m) {
38 help[i++] = arr[p1++];
39 }
40 while (p2 <= r) {
41 help[i++] = arr[p2++];
42 }
43 for (i = 0; i < help.length; i++) {
44 arr[L + i] = help[i];
45 }
46 return res;
47 }

浙公网安备 33010602011771号