小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。
举个简单的例子:给一个数组1 3 4 2 6
1左边比1小的数,没有;
3左边比3小的数,1;
4左边比4小的数,1、3;
2左边比2小的数,1;
6左边比6小的数,1 、3、 4、 2;
所以小和为1+1+3+1+1+3+4+2 = 16;
解题思路:一种可以用暴力排序的方法,从第一个元素开始,轮流找前面小于该元素的值,再从第二个元素开始,轮流找前面小于该元素的值,,,一直到最后一个元素,累加这些小于当前元素的值即可。
还有一种方法就是利用归并排序。将数组分为左半部分和右半部分,小和一共分为三部分,第一部分为左半部分的小和,第二部分为右半部分的小和,第三部分为左半部分和右半部分合并时产生的小和。如果左半部分元素N,小于右半部分元素M,那么从右半部分右指针P到右半部分最后R就有(R-P+1)*N小和。
实现代码如下:
# include <stdio.h>
int megerSortArr(int arr[], int left, int middle, int right)
{
int SortArr[right - left +1]; //用来排序
int p1 = left; //指向数组最左边的数
int p2 = middle + 1; //指向数组最右边的数
int sum = 0;
int i = 0;
while(p1 <= middle && p2 <= right)
{
sum += arr[p1] < arr[p2] ? (right - p2 +1) * arr[p1] : 0; //小和思想,第三部分—归并产生的小和
SortArr[i ++] = arr[p1] <arr[p2] ? arr[p1 ++]:arr[p2 ++]; //归并排序 ,谁小放谁
}
while(p1 < middle) //归并排序,右边越界
{
SortArr[i ++] = arr[p1 ++]; //左边部分全部复制
}
while(p2 < right) //归并排序,左边越界
{
SortArr[i ++] = arr[p2 ++]; //右边部分全部复制
}
return sum;
}
int megerSort(int arr[], int left, int right)
{
if(left == right) //递归的结束条件
{
return 0;
}
int middle = left + ((right - left) >> 1); //(left + right) / 2;
//左半部分小和 + 右半部分小和 + 数组归并排序产生的小和
return megerSort(arr,left,middle) + megerSort(arr,middle+1,right) + megerSortArr(arr,left,middle,right);
}
int megerSortSum(int arr[],int n)
{
return megerSort(arr,0,n-1);
}
int main(void)
{
// int a[] = {500, 22, 53, -158, 4, 86, 32, 33, 26, -70, 135, -34, 32, 543, 2500};
int a[] = {1,3,4,2,5,6};
int n; //存放数组a中元素的个数
int sum2 =0;
n = sizeof(a) / sizeof(a[0]); /*a[0]是int型, 占4字节*/
sum2 = megerSortSum(a,n);
printf("%d\t",sum2);
printf("\r\n");
return 0;
}
逆序对问题
在一个数组中,左边的数如果比右边的数大,则两个数构成一个逆序对,请打印逆序对的数目。
解题思路和求解小和问题一样,这里只要把上面的第11行代码换成sum += arr[p1] > arr[p2] ? (middle - p1 + 1) : 0;
即可。