算法之路_6、小和问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SIMPLE1995/article/details/86514704

一、问题

小和问题

       在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组 的小和。

例子: [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

二、解析

通过以上例子大致可以看出需要解决那些问题。我们算法中需要找到每一个数左边,比他小的数之和,然后计算每个数的小和之和。解决这个问题最简单粗暴的就是遍历,大致思想就是遍历每一个数,然后遍历每个数左侧比他小的数,并且计算和。最后计算总体小和。这样的算法的时间复杂度也很明显--O(N^2)。我们在前面的博文中介绍了递归思想,归并思想以及相应的算法。小和问题可以采用归并思想。

分析:(看不懂的同学可以往前翻阅递归以及归并两篇博文)

将数据样本按归并思想分为左右两部分,计算小和,最后归并时计算总体小和。(这里说的或许有些抽象,大家可以看看代码再做相应的理解)

三、代码

package algorithm;
/*
 *2019年1月9日
 */
public class SmallSum {

	public static int smallSum(int arr[]){
		//若数组为空或者只有一个元素 则直接返回0
		if (arr==null&&arr.length<2) {
			return 0;
		}
		
		
		return mergeSort(arr,0,arr.length-1);
	}
	
	public static int mergeSort(int[] arr, int l, int r) {
		//递归出口 当左右相等时 即只有一个元素时 返回0
		if (l==r) {
			return 0;
		}
		int mid=l+((r-l)>>1);
		//递归思想:左侧小和 +右侧小和 + 整体
		return mergeSort(arr, l, mid)
				+mergeSort(arr, mid+1, r)
				+merge(arr, l,mid, r);
	}
	public static int merge(int[] arr, int l, int mid, int r) {//最重要的求小和部分(归并部分)
		int help[]=new int[r-l+1];//辅助数组
		int i=0;
		int res=0;//记录小和
		int p1=l;//左边区域指针
		int p2=mid+1;//左边区域指针
		while (p1<=mid&&p2<=r) {
			/*
			 * 此时左右区域元素皆有序 所以 右区域所指元素大于左区域所指元素时
			 * 表明右区域所有元素都比左区域所指元素大  则小和+右区域元素个数*左区域所指元素
			 */
			res+=arr[p1]<arr[p2]?(r-p2+1)*arr[p1]:0;
			
			//归并排序部分代码
			help[i++]=arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
		}
		while(p2<=r){
			help[i++]=arr[p2++];
		}
		while(p1<=mid){
			help[i++]=arr[p1++];
		}
		for (int j = 0; j < help.length; j++) {
			arr[l+j]=help[j];
		}
		return res;//返回当前数组区域的小和
	}

	public static void main(String[] args) {
		//测试
		int arr[]={1,1,2,3};
		System.out.println(smallSum(arr));

	}

}

四、算法时间复杂度

同上篇归并排序算法博文。

猜你喜欢

转载自blog.csdn.net/SIMPLE1995/article/details/86514704