小和问题

描述
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。

例子
[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

首先想到的方法是遍历,两次for循环遍历数组,将每一个和他前面的数字比过去,只要比这个数小就与结果相加,最终可以在O(n^2)的时间复杂度上实现这道题。

int Comparator (int *a ,int n)
{
    if (a == NULL || n < 2) return 0;
    int sum=0;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<i;j++)
        {
            if (a[i] > a[j]) sum +=a[j];
        }
    }
    return sum;
}

 

但是n^2的复杂度过于高了,有没有方法可以简化呢?

有!
首先这个问题可以等同于归并排序,利用递归的思想,将数组不断查分成有序的一个个小序列,然后在每次merge归并的时候求值。

首先每次归并函数都是将两个已经排完序的数组进行外排,有两个指针p1,p2分别指向两个数组,把小的数字先放在一个help数组里然后把指针往后移,这时候我们发现每一个左数组的值进行放入temp时,它的值一定会比右指针后面的数字小,所以每一个数字一定会小于右数组(r-p2)个数,即小和的总和就需要加上(r-p2)*左指针p1指向的值。

int merge(int* &arr ,int l, int mid ,int r);
int Mergesort (int* &arr ,int l ,int r);
int SmallSum (int* &arr ,int n)
{
    if (arr ==NULL || n<2 ) return 0;

    return Mergesort(arr,0,n-1);
}

int Mergesort (int* &arr ,int l ,int r)
{
    if(l==r) return 0;

    int mid=l + ((r - l) >> 1);
    return Mergesort(arr,l,mid)+Mergesort(arr,mid+1,r)+merge(arr,l,mid,r);
}

// 1 3 9 || 2 4 7

int merge(int* &arr ,int l, int mid ,int r)
{
    int p1=l;
    int p2=mid+1;
    int i=0;
    int res=0;
    int* help = (int *)malloc (sizeof (int) * (r - l + 1));
    while(p1 <= mid && 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 <= mid)
    {
        help[i++] = arr[p1++];
    }
    while(p2 <= r)
    {
        help[i++] =arr[p2++];
    }

    for(int i=l;i<=r;i++)
    {
        arr[i]=help[i-l];
    }
    return res;
}

 

posted @ 2020-04-21 16:55  肉松松松松  阅读(338)  评论(0)    收藏  举报