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;
所以小和=14+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;
}