递归求解“小和“、普通方式求解“小和“

什么是"小和":

        在一个序列中,按照从左到右的方向,求每个位置上左边比它小的数的和,即“小和”。

        如: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除去子规模其它代码的执行复杂度。

        结论logb^a < d时     时间复杂度:O(N^2)

                :logb^a > d时    时间复杂度:O(N^{logb^a})

                :logb^a == d     时间复杂度:O(N^d*logN)

 在下列代码中:为你规模为N,b为2(分为两半操作,所以子规模为2),a也为2,d为1,

                master:T(N) = 2T(N/2) + O(N^1);

                                log2^2d比较,d=1,符合:logb^a == d     时间复杂度:O(N^1*logN)

这时d=1,所以利用递归求"小和"问题的时间复杂度为:O(N*logN)

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;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值