(算法)归并排序的延申,求小和问题以及逆序对问题。(超详细保姆级讲解)

求小和问题

  • 简述

 有一个数组,我们从左往右遍历,每移动到一位就将它左边的所有比它小的数字全部相加,最后的总和记为小和。下面是一个常规做法过程例子:

那么我们需要的是更快的做法。

  •  分析逻辑

我们仍然可以使用之前的归并排序,将其中merge的部分进行修改。

一下面为例,仍然是将该数组一分为二

图 1

图 2 

仍然是准备两个指标,和一个空列表help

看左边的1,3左边p1指向1右边p2指向3,进行比较,1<3,小和加1,把1存入help列表,指标p1右移出界(左边没有数字了),直接拷贝右边3进入help列表,再把help列表拷贝进数组。(help[1,3])

进入上层,p1指向1,p2指向2,1<2,小和加1,把1存入help列表,p1指向3,3>2,小和加0,把2存入help列表,右边没有数字了,把左边剩下的直接拷贝进help列表(help[1,2,3])

(5,6同理)

进入上一层,此时左边和右边都已经排好序,p1指向1,p2指向5,5>1,(因为右边也是有序的,所有得出5后面的数字都大于1,则直接进行下标计算即可)小和加1*2,把1存入help列表,p1指向2,5>2则小和加2*2,把2存入help列表,以此类推,最后整个数组有序,并求出了该数组的小和。

(ps:1.如果左边的数字大于右边,则将右边的数字存入help列表,p2指标右移一位,也就是谁小谁进入help;2.因为每次都是求完小和后再进行排序,所以排序不会影响结果)

  •  代码(Java):
 public static int process(int[] arr,int L,int R){
  if(L==R){
    return;
  }
  int M = L+((R-L)>>1);
  merge(arr,L,M,R);
  return process(arr,L,M)+process(arr,M+1,R)+merge(arr, L,M,R);
}  

public static int merge(int[] arr,int L,int R){
  int p1 = L;
  int p2 = M+1;
  int[] help = new int[R-L+1];
  int res =0;    //小和
  int i = 0;
  while(p1 <= M && p2 <= R){
    res += arr[p1] < arr[p2] ? (R - p2+1)*arr[p1]:0;    //左边小则该数乘右边下标数记小和
    help[i] = arr[p1] < arr[p2] ? arr[p1++]:arr[p2++];
  while(p1 <= M){
    help[i++] = arr[p1++]; 
  }
  while(p2 <= R){
    help[i++] = arr[p2++];
  }
  for(int i = 0,i < help.length,i++){
    arr[L+i] = help[i];    //把排好序的help拷贝到该次求小和数组范围
  return res;
  }
  }  
}

 逆序对问题

  • 简述

一个数组,左边的数比右边的数大则记为一个逆序对,它的本质和前面的求小和问题一样,我们同样只需要将merge修改一下即可。(求小和是求左边比右边小的)

 以上就是该数组中所有的逆序对。

猜你喜欢

转载自blog.csdn.net/qq_48860282/article/details/123939514