给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。 链接:
https://leetcode-cn.com/problems/array-partition-i/ (中文)https://leetcode.com/problems/array-partition-i/ (en)示例 1:
1 2 3 4 输入: [1,4,3,2] 输出: 4 解释: n 等于 2, 最大总和为 4 = min(1, 2) + min(3, 4).
提示:
n 是正整数,范围在 [1, 10000]. 数组中的元素范围在 [-10000, 10000]. 解题思路个人解题思路,观察给定的例子,很容易发现,对于一个给定的2 n 2n 2 n 大小的数组,只要在组成数对时每次都从数组选择最小的连两个数来组成n n n 个数对。所获得的1 到 n 的 m i n ( a i , b i ) 1到n的min(a_i,b_i) 1 到 n 的 m i n ( a i , b i ) 总和最大。
证明: 可以使用数学归纳法进行证明。
因此,首先考虑将数组进行排序,然后将所有奇数位置的元素加和起来就是结果.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public int arrayPairSum (int [] nums) { if (nums.length % 2 != 0 ) throw new IllegalArgumentException(); if (!isSorted(nums)) Arrays.sort(nums); int sum = 0 ; for (int i = 0 ; i < nums.length; i+=2 ) { sum += nums[i]; } return sum; } private static boolean isSorted (int [] arr) { for (int i = 0 ; i < arr.length-1 ; i++) { if (arr[i]>arr[i+1 ]) return false ; } return true ; } }
复杂度分析(Compelxity Analysis):
时间复杂度(Time complexity): O ( n l o g n ) O(nlogn) O ( n l o g n ) ,主要取决所使用的排序算法 空间复杂度(Space complexity): O ( 1 ) O(1) O ( 1 ) Submission status:
以上解法,通过所有测试用例,运行时间32 m s 32 ms 3 2 m s 击败28.65 28.65 2 8 . 6 5 %的提交(英文版leetcode)
可以清楚的了解到该问题还有更好的解法。 由于我的解题思路基于比较的排序算法,因此时间复杂度O ( n l o g n ) O(nlogn) O ( n l o g n ) 已是最优 ,需要考虑另外的解题思路。
如果不排序的情况下,得到结果。??? --> emmm… 没想到。。。
额…, 解题思路不变,还是基于排序,但是应用空间换时间 的思想,因此容易想到利用桶排序 对给定数组进行排序(毕竟数组元素范围已经给定− 10000 10000 -10000 ~ 10000 − 1 0 0 0 0 1 0 0 0 0 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution { public int arrayPairSum (int [] nums) { if (nums.length % 2 != 0 ) throw new IllegalArgumentException(); int [] bucket = new int [20001 ]; int maxElemt = Integer.MIN_VALUE; int minElemt = Integer.MAX_VALUE; for (int i = 0 ; i < nums.length; i++) { bucket[nums[i]+10000 ]++; if (nums[i] > maxElemt) maxElemt = nums[i]; if (nums[i] < minElemt) minElemt = nums[i]; } int result = 0 ; minElemt += 10000 ; maxElemt += 10000 ; boolean isOdd = true ; for (int i = minElemt; i <= maxElemt; i++) { if (bucket[i] > 0 ) { for (int j = 0 ; j < bucket[i]; j++) { if (isOdd) result += (i - 10000 ); isOdd = !isOdd; } } } return result; } }
代码简单解释,由于给定数组元素的范围为− 10000 -10000 − 1 0 0 0 0 ~ 10000 10000 1 0 0 0 0 , 因此桶排序所需要的总的容量为20000 20000 2 0 0 0 0 , 对给定数组出现的元素进行偏移(偏移量为10000 10000 1 0 0 0 0 )bucket[nums[i]+10000]++
,使用两个整型变量minElemt
和maxElemt
确定元素出现的范围, 使用flag 变量isOdd
判断当前处理元素是处于奇数位置还是偶数位置。
复杂度分析(Complexity Analysis) :
时间复杂度(Time complexity): O ( n ) O(n) O ( n ) , 就算是数组所有元素都相同的情况,也是只要遍历n n n 次 空间复杂度(Space complexity): O ( n ) O(n) O ( n ) 对于该题目来说,n 为 20000 n为20000 n 为 2 0 0 0 0 Submission status :
代码提交状况: 运行时间7 m s 7 ms 7 m s ,超过100 100 1 0 0 %的java
代码提交。
总结第一种解法是通用解法,可以适用于不同的给定数组元素范围,而第二种解法只适用于此题,当元素范围比较大时,所花费的空间代价高。