当前位置:   article > 正文

数组小和(单调和)_数组的单调和

数组的单调和
 
 

数组小和的定义如下:例如,数组s=[1,3,5,2,4,6] 在s[0]的左边小于或等于s[0]的数的和为0 在s[1]的左边小于或等于s[1]的数的和为1 在s[2]的左边小于或等于s[2]的数的和为1+3=4 在s[3]的左边小于或等于s[3]的数的和为1 在s[4]的左边小于或等于s[4]的数的和为1+3+2=6 在s[5]的左边小于或等于s[5]的数的和为1+3+5+2+4=15 所以s的小和为0+1+4+1+6+15=27 给定一个数组s,实现函数返回s的小和。

对于本题,最容易想到的就是通过二重循环暴力求解,时间复杂度O(n^2),代码如下:

  1. public class ArraySmallSum {
  2. public static void main(String[] args) {
  3. int[] arr = {1, 3, 5, 2, 4, 6};
  4. int smallSum = 0;
  5. for (int i = 1; i < arr.length; i++) {
  6. for (int j = 0; j < i; j++) {
  7. if (arr[j] < arr[i]) {
  8. smallSum += arr[j];
  9. }
  10. }
  11. }
  12. System.out.println(smallSum);
  13. }
  14. }

这样的时间复杂度显然是不能令人满意的,这里我们利用归并排序,在对有序子数组进行merge的同时,累加数组小和,时间复杂度O(nlogn),代码如下:

  1. public class ArraySmallSum {
  2. public static void main(String[] args) {
  3. int[] arr = {1, 3, 5, 2, 4, 6};
  4. System.out.println(getSmallSum(arr));
  5. }
  6. public static int getSmallSum(int[] arr) {
  7. if (arr == null || arr.length == 0) {
  8. return 0;
  9. }
  10. return mergeSortRecursion(arr, 0, arr.length - 1);
  11. }
  12. /**
  13. * 递归实现归并排序
  14. *
  15. * @param arr
  16. * @param l
  17. * @param r
  18. * @return 返回数组小和
  19. */
  20. public static int mergeSortRecursion(int[] arr, int l, int r) {
  21. if (l == r) { // 当待排序数组长度为1时,递归开始回溯,进行merge操作
  22. return 0;
  23. }
  24. int mid = (l + r) / 2;
  25. return mergeSortRecursion(arr, l, mid) + mergeSortRecursion(arr, mid + 1, r) + merge(arr, l, mid, r);
  26. }
  27. /**
  28. * 合并两个已排好序的数组s[left...mid]和s[mid+1...right]
  29. *
  30. * @param arr
  31. * @param left
  32. * @param mid
  33. * @param right
  34. * @return 返回合并过程中累加的数组小和
  35. */
  36. public static int merge(int[] arr, int left, int mid, int right) {
  37. int[] temp = new int[right - left + 1]; // 辅助存储空间 O(n)
  38. int index = 0;
  39. int i = left;
  40. int j = mid + 1;
  41. int smallSum = 0; // 新增,用来累加数组小和
  42. while (i <= mid && j <= right) {
  43. if (arr[i] <= arr[j]) {
  44. // 当前一个数组元素小于或等于后一个数组元素时,累加小和
  45. // s[i] <= s[j] -> s[i] <= s[j]...s[right]
  46. smallSum += arr[i] * (right - j + 1);
  47. temp[index++] = arr[i++];
  48. } else {
  49. temp[index++] = arr[j++];
  50. }
  51. }
  52. while (i <= mid) {
  53. temp[index++] = arr[i++];
  54. }
  55. while (j <= right) {
  56. temp[index++] = arr[j++];
  57. }
  58. for (int k = 0; k < temp.length; k++) {
  59. arr[left++] = temp[k];
  60. }
  61. return smallSum;
  62. }
  63. }

牛客网有道数组单调和,实际上和该题为同一道题。
另一道数组中的逆序对,与该题解法类似,只是merge时逆序对的累加条件和算法有所不同,此时merge操作的代码如下:

  1. /**
  2. * 合并两个已排好序的数组s[left...mid]和s[mid+1...right]
  3. *
  4. * @param arr
  5. * @param left
  6. * @param mid
  7. * @param right
  8. * @return 返回合并过程中累加逆序对
  9. */
  10. public static int merge(int[] arr, int left, int mid, int right) {
  11. int[] temp = new int[right - left + 1]; // 辅助存储空间 O(n)
  12. int index = 0;
  13. int i = left;
  14. int j = mid + 1;
  15. int inverseNum = 0; // 新增,用来累加数组逆序对
  16. while (i <= mid && j <= right) {
  17. if (arr[i] <= arr[j]) {
  18. temp[index++] = arr[i++];
  19. } else {
  20. // 当前一个数组元素大于后一个数组元素时,累加逆序对
  21. // s[i] > s[j] -> s[i]...s[mid] > s[j]
  22. inverseNum += (mid - i + 1);
  23. temp[index++] = arr[j++];
  24. }
  25. }
  26. while (i <= mid) {
  27. temp[index++] = arr[i++];
  28. }
  29. while (j <= right) {
  30. temp[index++] = arr[j++];
  31. }
  32. for (int k = 0; k < temp.length; k++) {
  33. arr[left++] = temp[k];
  34. }
  35. return inverseNum;
  36. }


作者:stevewang
链接:http://www.jianshu.com/p/3ab5033074f1
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/227461
推荐阅读
相关标签
  

闽ICP备14008679号