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     }

 

 

 

 

 

 

posted @ 2022-05-11 16:24  yzmarcus  阅读(66)  评论(0)    收藏  举报