归并排序:小和问题

归并排序:小和问题

在一个数组中,每一个元素左边比当前元素值小的元素值累加起来,叫做这个数组的小和
例如:[2, 3, 4, 1, 5]
2 左边比 2 小的元素:无
3 左边比 3 小的元素:2
4 左边比 4 小的元素:2,3
1 左边比 1 小的元素:无
5 左边比 5 小的元素:2, 3, 4, 1
小和small_sum = 2 + 2 + 3 + 2 + 3 + 4 + 1 = 17

解法

利用归并排序的性质,将数组二分,递归排序,在放入辅助数组的时候,由于左右两个数组都是排好序的,所以当右边有1个数大于左边某元素时,右边右面的数肯定大于该元素,只需将该元素乘以大于其元素个数,累加到sum上即可

如数组[2, 3, 4, 0, 5, 6, 1, 8]

递归调用归并排序,形成如下:

​ [2, 3, 4, 0] [5, 6, 1, 8]

[2, 3] [4, 0] [5, 6] [1, 8]

[2] [3] [4] [0] [5] [6] [1] [8]

由于递归的特性,先对2和3进行排序,放入辅助数组时, 左边2小于右边3,由于右边3后面没有其他数了,所以sum+=2x1,2和3从小到大排序。然后计算4和0,4大于0,不累加,直接进行排序。而后计算[2, 3]和[0, 4],放入辅助数组时,左边第一位2大于右边第一位0,由于左边是从小到大排序的,2都比0大了,2后面的数字不可能比0小,所以直接将0放入辅助数组,指针指向4, 2比4小,但是4后面没有更多数字了,所以2累加到sum上,sum+=2x1,然后2放入辅助数组,左边指向3, 3小于4,累加上sum+=3x1,然后完成排序,产生[0, 2, 3, 4]。右边同理,最后[0, 2, 3, 4]和[1,5,6,8]进行排序, 左边0和右边1进行比较,0比1小,由于右边是从大到小排好序的,所以后面的数字都比0大,直接累加sum+=0*4;其他的元素同理;

代码

#include <stdio.h>

int helperArr[10];

int Merge(int arr[], int l, int mid, int r)
{
    int i = l;
    int p1 = l;
    int p2 = mid + 1;
    int sum = 0;
    while (p1 <= mid && p2 <= r) {
        sum += arr[p1] < arr[p2] ? arr[p1] * (r - p2 + 1) : 0;
        helperArr[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
    }
    while (p1 <= mid) {
        helperArr[i++] = arr[p1++];
    }
    while (p2 <= r) {
        helperArr[i++] = arr[p2++];
    }

    for (; l <= r; l++) {
        arr[l] = helperArr[l];
    }
    return sum;
}

int MergeSort(int arr[], int l, int r)
{
    if (l == r) {
        return 0;
    }
    int mid = l + ((r - l) >> 1); // =(l+r)/2 因为l+r有可能会溢出,所以改成减法的方式
    return MergeSort(arr, l, mid) + MergeSort(arr, mid + 1, r) + Merge(arr, l, mid, r);
}

int SmallSum(int arr[], int size)
{
    if (arr == NULL || size < 2) {
        return 0;
    }
    return MergeSort(arr, 0, size - 1);
}

void PrintArr(int arr[], int size)
{
    for (int i = 0; i < size; ++i) {
        printf("%d ", arr[i]);
    }
    printf("
");
}

int main()
{
    int arr[10] = {6, 0, 5, 3, 15, 21, 13, 9, 12, 8};
    PrintArr(arr, 10);
    int ret = SmallSum(arr, 10);
    PrintArr(arr, 10);
    printf("小和是:%d", ret);
    return 0;
}
原文地址:https://www.cnblogs.com/causewang/p/12064308.html