赞
踩
上篇博客学习了归并排序,一种是遍历的方式实现,一种是迭代的方式实现,那么归并排序的思路只能用于排序嘛?也不是,这篇博客就记录一下归并排序对于求小和问题的思路与写法。
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。假设有这样一个数组 [3,4,1,5,2]
求这个数组的小和是多少?
按照小和的定义:
循环整个数组,依次左侧小于当前值的数累加起来。
暴力循环可以解决问题,但每次都要从头再次循环判断,性能有点低啊,有没有什么方法可以避免呢?先找一下规律吧。
这个有点眼熟啊,3跟4比,1跟5比,3和4 跟 1和5 比,上篇博客的合并merge 好像可以用上啊,上面的这个数组弄的不好,3、4有序了,1、5有序了,重新弄个数组说明吧:
按照merge的思路:
第一次操作产生了:5
第二次操作产生了:2+3
第三次操作产生了:1+1+1+2+2+2+3+3+4+4
总结:5+(2+3)+(1+1+1+2+2+2+3+3+4+4)= 33
3个1、4个2、3个3、2个4、1个5
和暴力解析一致,啊哈,这种迭代排序不必暴力循环快多了。
merge有实现小和的可能,可是该怎么实现呢?合并就不再画图了,直接说思路:
public static int smallSum(int[] arr) { if (arr == null || arr.length < 2) { return 0; } return splitSorting(arr, 0, arr.length - 1); } public static int splitSorting(int[] arr, int l, int r) { // 递归终止条件 if (l == r) { return 0; } int m = (l + r) / 2; // 对于左侧来说,l到中点 // 对于右侧来说,中点+1到右侧 return splitSorting(arr, l, m) + splitSorting(arr, m + 1, r) + merge(arr, l, m, r); } // 我需要一个返回值返回小和 public static int merge(int[] arr, int L, int M, int R) { // 我需要一个数组存放数据 int[] narr = new int[R - L + 1]; // 我需要三个指针,分别指向三个数据的头 int i = 0; int p1 = L; int p2 = M + 1; // 我需要一个循环体,结束条件是其中一个越界了,这里写的是循环条件哦,不是结束条件 // p1,p2都在各自的范围内 // 我需要一个ans记录小和 int ans = 0; while (p1 <= M && p2 <= R) { ans += arr[p1] < arr[p2] ? (R - p2 + 1) * arr[p1] : 0; narr[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; } // p1 越界,p1添加完了,剩下数据的就是p2 while (p2 <= R) { narr[i++] = arr[p2++]; } // p2 越界,p2添加完了,剩下数据的就是p1 while (p1 <= M) { narr[i++] = arr[p1++]; } // 合并后的narr数据,要放到原来的数组arr里的 for (int j = 0; j < narr.length; j++) { // L为左侧数据的开始,从L开始放数据 arr[L + j] = narr[j]; } return ans; }
上面的代码不完全是和上次一样的,特别需要提示一个地方,上次写的合并代码中,这个位置是小于等于,而本次的合并代码中,这个位置是小于,区别在于
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。