1. 什么是小和问题
小和问题是指,一个数组里的元素,从左往右看,某元素的左侧有小于该元素的,就把左侧该数记下来,然后依次记录所有类型的此数,最后累加起来即为小和
举例:
数组:2 1 3 4
当元素是2时,左边无元素,不选择累加的元素
当元素是1时,左边元素是2,大于1,不选择累加的元素
当元素是3时,左边元素是2和1,都比3小,所以选择累加元素为2,1
当元素是4时,左边元素是2,1,3,都比4小,所以选择累加元素是2,1,3
总小和:2+1+2+1+3=9
2. 小和问题面试题
题目:
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组【3, 2, 1, 7, 8, 9, 0】的小和
3. 对题目的思路
其实可以逆向理解题目,小和的一般算法是每一个数左边比当前数小的数累加起来,我们反过来看,只要该数的右边有数比该数大,那么该数*右边大于该数的数的数量为一部分小和
比方说,还是上面那个问题,数组:2 1 3 4,求小和
当元素是2时,右边元素1,3,4,比2大的元素有3,4,所以选择累加元素是22
当元素是1时,右边元素是3,4,大于1,所以选择累加的元素是12
当元素是3时,右边元素是4,比3大,所以选择累加元素为3*1
当元素是4时,右边无元素,所以无累加元素
总小和:22+12+3*1=9
说明这样是没有问题的
我们按逆向来理解题目,然后发现,这都是来和右侧的每一个数来比大小的,我们可以找一个算法来从左到右不遗漏的比谁大谁小,这就是归并算法
归并算法有个特点就是不断的比较大小并排序,我们可以在比较大小的时候来找到需要累加的元素,同时来进行排序方便拼合数组,目的是为了比较拼合数组时候各元素的大小
详细见下文
4. 程序
class Program
{
static void Main(string[] args)
{
int[] t = { 3, 2, 1, 7, 8, 9, 0 };
var p=Problem.StartSulte(t, 0, t.Length - 1);
Console.WriteLine(p);
Console.Read();
}
}
public class Problem1
{
//题目:小和问题和逆序对问题小和问题
//在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。
//例子:[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
//用逆向思维来看,就是针对某个数,看其右边有多少个数比其大,有一个则一个该数,有两个则两个该数,比如对于4,左侧有一个5比4大,那么加一个4
}
public class Problem
{
public static int StartSulte(int[] arr, int L, int R)
{
//通过递归
//取到无法分段时候,就是单个数
if (L == R)
{
return 0;
}
int M = L + ((R - L) >> 1);//中点的坐标
return StartSulte(arr, L, M)+StartSulte(arr, M + 1, R)+ MergeToAdd.Start(arr, L, M, R);
}
}
//在合并数组时候产生的小和元素
public static class MergeToAdd
{
public static int Start(int[] arr, int L, int M, int R)
{
int[] replace = new int[R-L+1];
int p1 = L;
int p2 = M + 1;
int sum = 0;
int i = 0;
while (p1<=M&&p2<=R)
{
sum += arr[p1] < arr[p2] ? (R - p2 + 1) * arr[p1] : 0;
replace[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1<=M)
{
replace[i++] = arr[p1++];
}
while (p2 <= R)
{
replace[i++] = arr[p2++];
}
for (int j = 0; j < replace.Length; j++)
{
arr[j+L] = replace[j];
}
//Console.WriteLine(sum);
return sum;
}
}
5. 对程序的理解
先用递归来循环遍历出每一个数来比较大小
需要知道的是,小和由三部分组成,一部分是左侧数组的小和,一部分是右侧数组的小和,还有一部分是merge的时候产生的小和,这三种小和累加才是最后的最终小和
比如说,数组:2 1 3 4
分为 2 1|3 4
先求2 1的小和,然后求3 4的小和,最后合并的时候,也就是merge的时候,会产生小和,把这三种类型的小和加起来,才是原数组的小和
所以在递归程序里,才会这样写:return StartSulte(arr, L, M)+StartSulte(arr, M + 1, R)+ MergeToAdd.Start(arr, L, M, R);
,递归就是按照中点分出若干个小数组,一直分到最后一步无法再分时候,开始来计算小和,每次都按return StartSulte(arr, L, M)+StartSulte(arr, M + 1, R)+ MergeToAdd.Start(arr, L, M, R);
来计算
那怎么merge?
在合并数组过程中,需要在比较过程中,来累加小和并且来从小到大来进行排序
怎么累加小和呢?左边数组元素来比较右边数组元素,如果左边数组元素小于右边,则左边该数就是一个小和元素
那为什么要排序呢?这个排序是一定不能省去的,不然无法进行小和的判断,按原数组来说,其组成数组之一左边数组在原数组的位置中肯定是在右边数组的左边,那么进行比较时候,如果右边数组不排序,那么将会漏小和,左边的数组某元素对于小和可能会失去