归并排序练习

1、小和问题

在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数的小和。
求一个数组的小和
举例:数组[1,3,4,2,5]
小和为 16
即1左边比它小的数,没有;3左边比它小的数,有,为1;
4左边比它小的数,有,为1,3;2左边比它小的数,有,为1;
5左边比它小的数,有,为1,3,4,2
小和=1+1+3+1+1+3+4+2=16

解法1:暴力法,遍历比较,计算小和,时间复杂度O(N*N)
代码:

public int Sum1(int[] arr)
{
    
    
    int sum = 0;
    for (int i = 1; i < arr.Length; i++)
    {
    
    
        for (int j = i - 1; j >= 0; j--)
        {
    
    
            if (arr[i] > arr[j])
                sum += arr[j];
        }
    }
    return sum;
}

解法2:
找每个数的右边有几个比当前数大
即对于1,右边有四个数比1大,14;对于3,右边有两个数比3大,32
对于4,右边有一个数比4大,41;对于2,右边有1个数比2大,21;
对于5,右边有0个数比5大,50;
所以小和=1
4+32+41+21+50=16
在这里插入图片描述
代码实现

public int MinSum(int[] arr)
{
    
    
    if (arr == null || arr.Length < 2)
        return 0;
    return Sum2(arr, 0, arr.Length - 1);
}

//在start到end间,既要排好序,又要求小和
public int Sum2(int[] arr,int start,int end)
{
    
    
    if (start >= end)
        return 0;
    int mid = start + ((end - start) >> 1);
    return Sum2(arr, start, mid)
           +
           Sum2(arr, mid + 1, end)
             +
           Merge(arr, start, mid, end);
}

public int Merge(int[] arr, int start, int mid, int end)
{
    
    
    int sum = 0;
    int p1 = start;
    int p2 = mid + 1;
    int i = 0;
    int[] help = new int[end-start+1];
    while (p1 <= mid && p2 <= end)
    {
    
    
        sum += arr[p1]<arr[p2]?(end-p2+1) * arr[p1]:0;//左侧小于右侧,才加入sum
        //当左右两个指向的数相等时,要先拷贝右侧上的数字,这时是不加入sum中(不产生小和)【与归并排序不同的地方】
        //(end-p2+1) * arr[p1],此时左右侧都是排好序的状态,顺序从小到大,
        //若是右侧某一个数大于左侧数(当前数),那么右侧某一数后面的数也是大于当前数的
        help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
    }

    while (p1 <= mid)
    {
    
    
        help[i++] = arr[p1++];
    }

    while (p2 <= end)
    {
    
    
        help[i++] = arr[p2++];
    }
    Array.Copy(help,0,arr,start,help.Length);//每一次的排序是一定要做的
    return sum;
}

2、逆序对问题

在一个数组中,左边的数如果比右边的数大,则这两个数构成一个逆序对
请打印所有逆序对
例子 数组[3,2,4,5,0]
逆序对
3,2;3,0;2,0;4,0;5,0
思路:找右边有多少个数比当前数小
与上一题区别,在排序时要按照从大到小顺序来排序
代码实现:

public int NiXu(int[] arr)
{
    
    
    if (arr == null || arr.Length < 2)
    {
    
    
        return 0;
    }
    return NiXuDiGui(arr, 0, arr.Length - 1);
}

public int NiXuDiGui(int[] arr, int start, int end)
{
    
    
    if (start == end)
        return 0;
    int mid = start + ((end - start) >> 1);
    return NiXuDiGui(arr, start, mid)
           +
           NiXuDiGui(arr, mid + 1, end)
           +
           Merge(arr, start, mid, end);
}

public int Merge(int[] arr, int start, int mid, int end)
{
    
    
    int index = 0;
    int res = 0;
    int p1 = start;
    int p2 = mid+1;
    int i = 0;
    int[] help = new int[end-start+1];
    while (p1 <= mid && p2 <=end)
    {
    
    
        index = res;
        res += arr[p1]>arr[p2] ? (end-p2+1) : 0;
        if(index!=res)
        {
    
    
            //打印
            for (int j = p2; j <= end; j++)
            {
    
    
                Console.WriteLine("第"+res+"组逆序数为:"+arr[p1]+","+arr[j]);
            }
        }
        help[i++] = arr[p1] > arr[p2] ? arr[p1++] : arr[p2++];//从大到小顺序
    }

    while (p1 <= mid)
    {
    
    
        help[i++] = arr[p1++];
    }

    while (p2 <= end)
    {
    
    
        help[i++] = arr[p2++];
    }
    Array.Copy(help,0,arr,start,help.Length);
    return res;
}

猜你喜欢

转载自blog.csdn.net/weixin_51565051/article/details/129869701