小和问题和逆序对问题

小和问题

在一个数组中, 每一个数左边比当前数小的数累加起来, 叫做这个数组的小和。 求一个数组的小和。
例子:[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

解题思路

使用归并排序来进行求和,在归并的时候把数组分成左右两个,在归并排序进行左右两个数组进行合并排序的时候进行计算。如果左边数组元素N,小于右边数组元素M,那么从右边数组右指针P到右边数组最后R就有(R-P+1)个N,依次累计相加,最后求出最小和。

代码

//总体思路和归并排序一致,在合并的过程中计算小和
public static int smallSum(int[] arr){
    if(arr==null || arr.length<2){
        return 0;
    }
    return mergeSort(arr,0,arr.length-1);
}

public static int mergeSort(int[] arr,int l,int r){
    if(l==r){
        return 0;
    }
    int mid=l+((r-l)>>2);
    return mergeSort(arr,l,mid)
            +mergeSort(arr,mid+1,r)
            +merge(arr,l,mid,r);
}

public static int merge(int[] arr,int l,int m,int r){
    int[] help=new int[r-l+1];
    int i=0;
    int p1=l;
    int p2=m+1;
    int res=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 (i=0;i<help.length;i++){
        arr[l+i]=help[i];
    }
    return res;
}

逆序对问题

在一个数组中, 左边的数如果比右边的数大, 则这两个数构成一个逆序对, 请打印所有逆序对。

解题思路

所谓逆序对就是[4,2],[4,1],[5,1]…,也就是左边比右边大,那么有多少个逆序对就是,中间位置mid减去左指针下坐标P1+1个逆序对,也就是(mid-P1+1)个逆序对,把逆序对相加进行返回就是共有多少逆序对。

代码

public static void reverseOrder(int[] arr) {
    if (arr==null || arr.length<2) {
        return ;
    }
     mergeSort(arr,0,arr.length-1);
}

public static int mergeSort(int[] arr, int l, int r) {        
    if (l == r) {
        return 0;
    }
    int mid = (l+r)/2;
    int k = mergeSort(arr, l, mid)+mergeSort(arr, mid+1, r)+merge(arr,l,mid,r);
    System.out.println("merge总逆序数:"+k);
    return k;
}

public static int merge(int[] arr, int l, int mid, int r) {
    int merge_res=0;
    int[] help = new int[r - l + 1];   //help的长度不是一个大的N 而是每次分治 的长度
    int i=0;
    int p1 = l;
    int p2 = mid+1;
    
    while(p1 <= mid && p2 <= r) {
        if ( arr[p2] < arr[p1] ) {    //说明p2此时比p1中剩下的元素都小    
            merge_res += (mid-p1+1);  //核心 
            for(int k=0;k<mid-p1+1;k++) //打印此时的逆序对
                    System.out.println(arr[p1+k]+" "+arr[p2]);
        }
        help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++] ;
    }
    while(p1<=mid) {
        help[i++] = arr[p1++];
    }
    while (p2<=r) {
        help[i++] = arr[p2++];
    }
    //拷贝到 arr数组
    for (int j = 0; j < help.length; j++) {
        arr[l+j] = help[j];
    }
    System.out.println("merge_res:"+merge_res);
    return merge_res;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值